diff --git a/README.md b/README.md index 7ed11b5..5f49f35 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,122 @@ +**You can easily run this project buy using docker branch instead of creating locally.** + +# From leader of KOU-Mekatronom +Hi, + +Attached are the codes I developed for the KOU-Mekatronom team, which I was a part of from 2021 to 2024, and where I served as the team captain since 2022. Last year, we were accepted into the BOSCH Future Mobility Challenge competition, held in Romania. Our team was the only one from Turkey to reach the finals (top 24) among 160 international teams. + +This project includes everything I have developed since May 2023. While working on this project, I was also employed at [Saha Robotic](https://www.linkedin.com/company/saha-robotik/mycompany/), so I could only develop it during the evenings after work and on weekends. Because of this, I am aware that some parts of the project are hard-coded for specific competition scenarios and are not fully complete. However, I believe I did everything I could within the time I had, and I have no regrets. + +Currently, the project is written in Python, but I have started porting it to C++ with cleaner code. Soon, I will stop contributing to this project without further updates. The reason is that I have graduated and would like to move on to new challenges, passing this project on to new team members. + +This project works in both real-world applications and simulation. In the real system, we used ZED 2i, an encoder, and an IMU for localization, and we plan to add UWB for global reference. Additionally, instead of LiDAR, we used point cloud data from the ZED for obstacle detection, but all other codes remain the same. + +**Thanks for reading** + +# Project explanation + +The autonomous driving and connectivity algorithms on 1/10 scale vehicles, provided by the company, to navigate in a designated environment simulating a miniature smart city. This project designed for the competation named as BOSCH FUTURE MOBILITY CHALLANGE. + +# How the algorithms are work ? + +We have [node graph list](https://github.com/Renbago/autonomus_vehicle/blob/main/src/example/config/fixed2.graphml) (also you can create your node base graph system) +we are selecting the [start and targetnode](https://github.com/Renbago/autonomus_vehicle/blob/ba1dc0e1d733606ee26514bc1f55c89231d02a76/src/example/include/mekatronom/MpcNode.hpp#L94-L95) (It will define from launch file later currently we are changing in python code and header file as i mentioned that.) and [djikstra](https://github.com/Renbago/autonomus_vehicle/blob/main/src/example/include/mekatronom/utilities/djikstra.h) is solving the closest path. You can add [excluded nodes](https://github.com/Renbago/autonomus_vehicle/blob/ba1dc0e1d733606ee26514bc1f55c89231d02a76/src/example/include/mekatronom/MpcNode.hpp#L97C1-L97C55). If there is a [obstacle](https://github.com/Renbago/autonomus_vehicle/tree/main/src/obstacle_detector) we are detecting this center_x, center_y and velocity then giving this input to [mpc_running](https://github.com/Renbago/autonomus_vehicle/blob/ba1dc0e1d733606ee26514bc1f55c89231d02a76/src/example/include/mekatronom/utilities/mpc_running.h#L181) if the obstacles position matches with the node graph system we are detecting it as obstacle and calling djikstra again. If the node has pass_through, we are crossing the left lane but if its set **false** we are waiting the until [obstacle moved](https://github.com/Renbago/autonomus_vehicle/blob/5e3e2af504190099f591417b3469074eb4eb44af/src/example/include/mekatronom/utilities/traffic_sign_manager.h#L41C1-L41C59) if obstacles moved we are triggering djikstra again and the path will get update. Non linear mpc will be follow the next node_id. + +There is a few behaviour states and will be updated coming data's from yolo and obstacles else the state will be ```keep_lane``` we have different parametrs for different states those parameters has been [tuned](https://github.com/Renbago/autonomus_vehicle/blob/ba1dc0e1d733606ee26514bc1f55c89231d02a76/src/example/include/mekatronom/utilities/mpc_start_setting.h#L54-L84) for real time system. And also there is a **behaviour_timer** section. This working same logic with **watchdog_timer** if something went wrong or some behaviours should be executed, by **behaviour_callback** we are able to execute those scenerios. + +# Whats included ? +I also wanted to share The foundation of this code was inspired by and further developed upon the work in the following GitHub repositories: + +This project using [Non-linear MPC with CasADi](https://github.com/MMehrez/MPC-and-MHE-implementation-in-MATLAB-using-Casadi/tree/master/workshop_github), [Dynamic/static obstacle detection](https://github.com/jk-ethz/obstacle_detector), [robot_localization](https://github.com/cra-ros-pkg/robot_localization), Rviz visualization, djikstra and [yolo](https://github.com/ultralytics/ultralytics). + +The all of the implementation's which i mentioned are currently work on [mpc.py](https://github.com/Renbago/autonomus_vehicle/blob/devel/src/example/src/mpc.py), +but the all of the python codes has not been cleaned and a bit dirty code. But while preparing the competation I was not care of the clean writing. Mostly settings are inside of ```mpc.py``` you can just execute it. + +**The cpp part is not fully finished. Currently we are supporting those futures;** +Dynamic Static obstacle detection, Non linear MPC with waypoint base system, robot_localization path regeneration by obstacles. Soon traffic_sign_scenerios will be wroted so selected nodes or the table data's from yolo we will be change the mpc parameters. + +## The simulation and real life outputs from project + + [![KOU-Mekatronom Youtube Channel](https://youtube.com/playlist?list=PLDE_vDxu0Gkk-s3ndTqIScKTHSvL8dt0m&si=bbp9Qc9xVI-1Tctj)](https://youtube.com/playlist?list=PLDE_vDxu0Gkk-s3ndTqIScKTHSvL8dt0m&si=bbp9Qc9xVI-1Tctj) + +## Prerequisites + +Before you begin, ensure you have met the following requirements: + +- **Docker**: You need to have Docker installed on your system. Follow the official [Docker installation guide for Ubuntu](https://docs.docker.com/engine/install/ubuntu/). +- **Visual Studio Code**: Install Visual Studio Code, if you haven't already. +- **Docker Extension for Visual Studio Code**: Install the Docker extension from the Visual Studio Code marketplace. + +# Getting Started + +Follow these steps to get your project up and running in a Docker environment. + +### 1. Clone the Repository +```git clone -b docker https://github.com/Renbago/autonomus_vehicle.git``` + +### 2. Build Docker Images + +1.You can use this command if you suspect that a cached layer is causing issues (e.g., dependencies not updating properly), or if you made changes that Docker’s layer cache might not recognize (such as updates to apt-get or similar commands inside the Dockerfile); + +```docker-compose build --no-cache``` + +2.If you've just made changes to your application code or Dockerfile and want to rebuild the image and start the containers is sufficient and faster; + +```docker-compose up -d --build``` + +### 3. Start Docker Containers: +```docker-compose up```, + +if you want access from terminal run this command ```docker exec -it autonomous_ws /bin/bash``` + +### 4. Attach Visual Studio Code to Docker Container + +1.Open Visual Studio Code. + +2.Open the Docker extension panel (usually on the left sidebar). + +3.Find the running container for your project. + +4.Right-click on the container and select **Attach Visual Studio Code**. + +This will open a new instance of Visual Studio Code that is connected to the file system within the Docker container, allowing you to develop and debug your application directly inside the container. + +# For running the project: +at one terminal: +```roslaunch sim_pkg map_with_car.launch``` + +the second terminal: +```rosrun example mpc_node``` + +or + +```rosrun example mpc.py``` + +# IMPORTANT +The ```mpc_node.cpp``` version has not been fully finished, you can run ```rosrun example mpc.py``` when launched ```map_with_car.launch``` also obstacle_detector will be executed. + +# + +# Outputs from Gazebo + -From new parkour: +# From new parkour: -From added RVIZ: - +# From added RVIZ: +# Obstacle detection + + + +# Traffic lights plugin: + + + # BFMC Simulator Project The project contains the entire Gazebo simulator. @@ -25,10 +133,23 @@ From KOU-Mekatronom team: - Added urdf and lidar.sdf - It has laser_scan now topic name is ```/automobile/scan``` for bostacle_detection. - Added TF2 package the tf tree visualization ```frames.pdf``` +- Added traffic lights publisher, ```src/sim_pkg/launch/sublaunchers/traffic_lights.launch``` +- In your main code you need to subscribe ``` automobile/trafficlight/master,slave,start topics``` Tips on how to install and work on it, can be found in the - ## The documentation is available in details here: [Documentation](https://bosch-future-mobility-challenge-documentation.readthedocs-hosted.com/data/simulator.html) + +This project includes the algorithms has been made from KOU-Mekatronom:: +- It has robot_localization package, you can fuse the gps and IMU data easily. +- Robot_localization package config path is ```src/example/config/ekf_localization.yaml``` +- Added urdf and lidar.sdf +- It has laser_scan now topic name is ```/automobile/scan``` for bostacle_detection. +- Added TF2 package the tf tree visualization ```frames.pdf``` +- Added traffic lights publisher, ```src/sim_pkg/launch/sublaunchers/traffic_lights.launch``` +- In your main code you need to subscribe ``` automobile/trafficlight/master,slave,start topics``` + +# Contributors +[Mehmet Baha Dursun](https://github.com/Renbago) diff --git a/autonomus_vehicle.docx.pdf b/autonomus_vehicle.docx.pdf new file mode 100644 index 0000000..735c7e3 Binary files /dev/null and b/autonomus_vehicle.docx.pdf differ diff --git a/obstacle_detection.png b/obstacle_detection.png new file mode 100644 index 0000000..f517814 Binary files /dev/null and b/obstacle_detection.png differ diff --git a/rviz.png b/rviz.png index b3b00f5..f517814 100644 Binary files a/rviz.png and b/rviz.png differ diff --git a/src/depends/install_deps.sh b/src/depends/install_deps.sh new file mode 100644 index 0000000..b7f9c76 --- /dev/null +++ b/src/depends/install_deps.sh @@ -0,0 +1,54 @@ +#!/bin/bash +set -e +REPO_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )/../" && pwd )" +TMP_DIR="/tmp" + +install_ipopt() { + echo "Prepare to install IPOPT ..." + IPOPT_URL="https://e.coding.net/tmp-code/ipopt-3.12.4.git" + sudo apt-get -y install \ + gfortran \ + cmake \ + build-essential \ + gcc \ + g++ + sudo ldconfig + if ( ldconfig -p | grep libipopt ); then + echo "Ipopt is already installed......." + else + echo "Start installing Ipopt, version: 3.12.4 .........." + pwd + cd $TMP_DIR + pwd + rm -rf ipopt-3.12.4 && git clone "$IPOPT_URL" && cd ipopt-3.12.4 + # configure,build and install the IPOPT + echo "Configuring and building IPOPT ..." + ./configure --prefix /usr/local + make -j$(nproc) + make test + sudo make install + if (grep 'export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH' $HOME/.bashrc); then + echo "LD_LIBRARY_PATH has been set." + else + echo 'export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH' >> $HOME/.bashrc + fi + sudo ldconfig + echo "IPOPT installed successfully" + source $HOME/.bashrc + fi + cd $REPO_DIR +} + +main() { + #sudo apt-get update + install_ipopt + # install_cppad + # install_benchmark + # install_glog + # install_gflags + # install_grid_map + # install_osqp_eigen + # clone_other_ros_pkgs +} + +main \ No newline at end of file diff --git a/src/example/CMakeLists.txt b/src/example/CMakeLists.txt index f8dce85..df9ace0 100644 --- a/src/example/CMakeLists.txt +++ b/src/example/CMakeLists.txt @@ -1,18 +1,92 @@ cmake_minimum_required(VERSION 3.0.2) project(example) +# Ensure C++17 is used +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_WARN_DEPRECATED OFF) +set(CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION OFF) + +# Standard C++ settings +if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + add_compile_options(-std=c++17 -Wall -Wextra) +endif() + + +# Find required packages +find_package(Casadi REQUIRED) +find_package(pugixml REQUIRED) +find_package(Eigen3 REQUIRED) + +message(STATUS "Casadi include dir: ${Casadi_INCLUDE_DIRS}") +message(STATUS "Casadi libraries: ${Casadi_LIBRARIES}") + +# Find catkin packages find_package(catkin REQUIRED COMPONENTS - rospy + geometry_msgs std_msgs + rospy + roscpp + sensor_msgs utils + cv_bridge + image_transport + message_generation ) +# Add message files +add_message_files( + FILES + MekatronomYolo.msg +) + +# Generate messages +generate_messages( + DEPENDENCIES + std_msgs +) + +# Catkin specific configuration catkin_package( -# INCLUDE_DIRS include + INCLUDE_DIRS include + LIBRARIES ${PROJECT_NAME} + CATKIN_DEPENDS geometry_msgs std_msgs rospy utils roscpp sensor_msgs message_runtime + DEPENDS Eigen3 ) +# Include directories include_directories( -# include + include ${catkin_INCLUDE_DIRS} + ${OpenCV_INCLUDE_DIRS} + ${EIGEN3_INCLUDE_DIR} +) + +# Add executable +add_executable(mpc_node scripts/mpc_node.cpp scripts/MpcNode.cpp) +add_dependencies(mpc_node ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS}) + +# Link libraries +target_link_libraries(mpc_node + ${catkin_LIBRARIES} + casadi + ${Casadi_LIBRARIES} + ${OpenCV_LIBRARIES} + pugixml +) + +# Installation +install(TARGETS mpc_node + RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} + LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} + ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} +) + +install(DIRECTORY include/${PROJECT_NAME}/ + DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION} ) +# Optionally install config and launch directories +# install(DIRECTORY config launch +# DESTINATION share/${PROJECT_NAME} +# ) diff --git a/src/example/config/Competition_track_graph.graphml b/src/example/config/Competition_track_graph.graphml new file mode 100644 index 0000000..5f51a09 --- /dev/null +++ b/src/example/config/Competition_track_graph.graphml @@ -0,0 +1,3788 @@ + + + + + + + + + 0.0 + 0.0 + + + 0.5 + 0.0 + + + 1.0 + 0.0 + + + 1.5 + 0.0 + + + 2.0 + 0.0 + + + 2.5 + 0.0 + + + 3.0 + 0.0 + + + 3.5 + 0.0 + + + 4.0 + 0.0 + + + 4.5 + 0.0 + + + 5.0 + 0.0 + + + 5.5 + 0.0 + + + 6.0 + 0.0 + + + 6.5 + 0.0 + + + 7.0 + 0.0 + + + 7.5 + 0.0 + + + 8.0 + 0.0 + + + 8.5 + 0.0 + + + 9.0 + 0.0 + + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + + + \ No newline at end of file diff --git a/src/example/config/Competition_track_graph.graphml:Zone.Identifier b/src/example/config/Competition_track_graph.graphml:Zone.Identifier new file mode 100644 index 0000000..053d112 --- /dev/null +++ b/src/example/config/Competition_track_graph.graphml:Zone.Identifier @@ -0,0 +1,3 @@ +[ZoneTransfer] +ZoneId=3 +HostUrl=about:internet diff --git a/src/example/config/fixed2.graphml b/src/example/config/fixed2.graphml new file mode 100644 index 0000000..8c1620f --- /dev/null +++ b/src/example/config/fixed2.graphml @@ -0,0 +1,4068 @@ + + + + + + + + + + 9.34 + 0.54 + + + 10.04 + 0.54 + + + 10.99 + 0.54 + + + 11.71 + 0.54 + + + 12.41 + 0.54 + + + 9.34 + 1.3 + + + 10.03 + 1.3 + + + 10.73 + 1.3 + + + 11.41 + 1.3 + + + 13.11 + 1.3 + + + + + 4.17 + 6.89 + + + 4.17 + 6.52 + + + 4.76 + 5.94 + + + 5.13 + 5.94 + + + 5.71 + 6.89 + + + 5.13 + 7.48 + + + 4.76 + 7.48 + + + 4.94 + 6.71 + + + 4.94 + 6.71 + + + 4.94 + 6.71 + + + 4.79 + 6.52 + + + 1.93 + 6.89 + + + 1.93 + 6.52 + + + 2.52 + 5.93 + + + 2.88 + 5.93 + + + 3.47 + 6.52 + + + 3.47 + 6.89 + + + 2.7 + 6.70 + + + 2.7 + 6.70 + + + 2.7 + 6.70 + + + 2.7 + 6.70 + + + 1.93 + 4.02 + + + 1.93 + 3.65 + + + 2.51 + 3.07 + + + 2.88 + 3.07 + + + 3.47 + 3.65 + + + 3.47 + 4.02 + + + 2.88 + 4.61 + + + 2.51 + 4.61 + + + 2.7 + 3.84 + + + 2.7 + 3.84 + + + 2.7 + 3.84 + + + 2.7 + 3.84 + + + 4.17 + 4.02 + + + 4.17 + 3.65 + + + 4.75 + 3.06 + + + 5.12 + 3.06 + + + 5.71 + 4.02 + + + 5.12 + 4.60 + + + 4.75 + 4.60 + + + 4.94 + 3.83 + + + 4.94 + 3.83 + + + 4.94 + 3.83 + + + 4.94 + 3.83 + + + 14.71 + 4.01 + + + 14.71 + 3.64 + + + 15.29 + 3.05 + + + + + + + 16.25 + 4.01 + + + + + + + 15.66 + 4.60 + + + 15.29 + 4.60 + + + 15.48 + 3.83 + + + 15.48 + 3.83 + + + 15.48 + 3.83 + + + 15.48 + 3.83 + + + 1.94 + 0.36 + + + 1.94 + 0.73 + + + 2.51 + 1.30 + + + 2.88 + 1.30 + + + 3.45 + 0.36 + + + 3.45 + 0.73 + + + 2.69 + 0.54 + + + 2.69 + 0.54 + + + 2.69 + 0.54 + + + 0.35 + 10.57 + + + 0.72 + 10.57 + + + 1.29 + 10.00 + + + 1.29 + 9.63 + + + 0.35 + 9.06 + + + 0.72 + 9.06 + + + 0.53 + 9.81 + + + 0.53 + 9.81 + + + 0.53 + 9.81 + + + 0.35 + 7.48 + + + 0.72 + 7.48 + + + 1.29 + 6.90 + + + 1.29 + 6.54 + + + 0.35 + 5.96 + + + 0.72 + 5.96 + + + 0.53 + 6.72 + + + 0.53 + 6.72 + + + 0.53 + 6.72 + + + 0.35 + 4.59 + + + 0.72 + 4.59 + + + 1.29 + 4.02 + + + 1.29 + 3.65 + + + 0.35 + 3.08 + + + 0.72 + 3.08 + + + 0.53 + 3.84 + + + 0.53 + 3.84 + + + 0.53 + 3.84 + + + 2.88 + 1.68 + + + 2.88 + 2.06 + + + 2.88 + 2.44 + + + 2.89 + 2.82 + + + 2.51 + 2.69 + + + 2.51 + 2.31 + + + 2.52 + 1.93 + + + 2.51 + 1.55 + + + 1.56 + 0.72 + + + 1.18 + 0.76 + + + 0.87 + 0.98 + + + 0.73 + 1.34 + + + 0.73 + 1.72 + + + 0.73 + 2.10 + + + 0.73 + 2.48 + + + 0.74 + 2.86 + + + 0.35 + 2.70 + + + 0.35 + 2.32 + + + 0.34 + 1.94 + + + 0.34 + 1.56 + + + 0.39 + 1.19 + + + 0.51 + 0.83 + + + 0.77 + 0.56 + + + 1.11 + 0.39 + + + 1.48 + 0.32 + + + 3.79 + 4.02 + + + 3.85 + 3.65 + + + 1.55 + 4.02 + + + 1.67 + 3.65 + + + 2.88 + 4.99 + + + 2.88 + 5.37 + + + 2.88 + 5.75 + + + 2.52 + 5.55 + + + 2.52 + 5.17 + + + 2.52 + 4.79 + + + 0.73 + 4.97 + + + 0.74 + 5.35 + + + 0.74 + 5.73 + + + 0.35 + 5.59 + + + 0.34 + 5.21 + + + 0.35 + 4.83 + + + 1.67 + 6.53 + + + 1.55 + 6.88 + + + 3.85 + 6.52 + + + 3.79 + 6.88 + + + 5.13 + 7.86 + + + 5.11 + 8.24 + + + 5.04 + 8.61 + + + 4.9 + 8.97 + + + 4.71 + 9.30 + + + 4.46 + 9.59 + + + 4.17 + 9.84 + + + 3.84 + 10.03 + + + 3.48 + 10.14 + + + 3.1 + 10.19 + + + 2.72 + 10.19 + + + 2.35 + 10.11 + + + 1.97 + 10.03 + + + 1.59 + 10.01 + + + 1.67 + 9.61 + + + 2.05 + 9.62 + + + 2.4 + 9.75 + + + 2.77 + 9.84 + + + 3.15 + 9.83 + + + 3.52 + 9.74 + + + 3.87 + 9.59 + + + 4.17 + 9.37 + + + 4.42 + 9.08 + + + 4.64 + 8.78 + + + 4.73 + 8.41 + + + 4.76 + 8.04 + + + 4.76 + 7.66 + + + 0.72 + 7.86 + + + 0.73 + 8.24 + + + 0.73 + 8.62 + + + 0.34 + 8.68 + + + 0.34 + 8.30 + + + 0.34 + 7.92 + + + 3.83 + 0.35 + + + 4.21 + 0.35 + + + 4.59 + 0.35 + + + 4.97 + 0.35 + + + 5.34 + 0.35 + + + 5.72 + 0.35 + + + 6.1 + 0.36 + + + 6.44 + 0.54 + + + 6.65 + 0.86 + + + 6.66 + 1.24 + + + 6.63 + 1.62 + + + 6.62 + 2.00 + + + 6.63 + 2.38 + + + 6.63 + 2.76 + + + 6.62 + 3.14 + + + 6.61 + 3.52 + + + 6.71 + 4.05 + + + 6.1 + 4.02 + + + 4.76 + 2.68 + + + 4.76 + 2.31 + + + 4.76 + 1.93 + + + 4.75 + 1.55 + + + 4.75 + 1.17 + + + 4.8 + 0.67 + + + 4.35 + 0.77 + + + 3.97 + 0.75 + + + 4.8 + 0.44 + + + 5.07 + 0.93 + + + 5.14 + 1.31 + + + 5.16 + 1.69 + + + 5.15 + 2.07 + + + 5.12 + 2.45 + + + 5.14 + 2.83 + + + 5.13 + 4.98 + + + 5.13 + 5.37 + + + 5.14 + 5.75 + + + 4.75 + 5.56 + + + 4.75 + 5.18 + + + 4.73 + 4.80 + + + 7.03 + 0.89 + + + 7.41 + 0.90 + + + 7.79 + 0.91 + + + 8.17 + 0.90 + + + 8.54 + 0.90 + + + 8.92 + 0.90 + + + 9.3 + 0.90 + + + 9.68 + 0.91 + + + 10.06 + 0.91 + + + 10.44 + 0.92 + + + 10.82 + 0.92 + + + 11.2 + 0.93 + + + 11.58 + 0.93 + + + 11.95 + 0.93 + + + 12.33 + 0.93 + + + 12.71 + 0.93 + + + 13.09 + 0.92 + + + 13.47 + 0.92 + + + 13.85 + 0.92 + + + 14.23 + 0.92 + + + + + 14.61 + 0.93 + + + + + 14.99 + 0.94 + + + 15.36 + 0.94 + + + 15.74 + 0.94 + + + 16.12 + 0.94 + + + 16.5 + 0.92 + + + 16.84 + 1.10 + + + 17.08 + 1.39 + + + 17.08 + 1.77 + + + 17.08 + 2.15 + + + 17.08 + 2.53 + + + 17.08 + 2.91 + + + + + + + + 17.08 + 3.30 + + + 17.02 + 3.67 + + + 16.71 + 3.90 + + + 15.3 + 2.68 + + + 15.3 + 2.30 + + + 15.28 + 1.92 + + + 15.29 + 1.54 + + + 15.32 + 1.16 + + + 11.77 + 2.05 + + + 12.15 + 2.05 + + + 12.53 + 2.03 + + + 12.91 + 1.99 + + + 13.24 + 1.80 + + + 13.55 + 1.58 + + + 13.85 + 1.35 + + + 14.14 + 1.11 + + + 14.32 + 4.02 + + + 13.94 + 4.01 + + + 13.56 + 4.01 + + + 13.18 + 4.01 + + + 12.8 + 4.01 + + + 12.42 + 4.01 + + + 12.04 + 4.01 + + + 11.66 + 4.00 + + + 11.28 + 4.00 + + + 10.9 + 4.00 + + + 10.51 + 4.01 + + + 10.13 + 4.01 + + + 9.75 + 4.01 + + + 9.37 + 4.01 + + + 8.99 + 4.01 + + + 8.61 + 4.01 + + + 8.23 + 4.01 + + + 7.85 + 4.01 + + + 7.47 + 4.01 + + + 7.09 + 4.01 + + + 6.71 + 4.05 + + + 6.64 + 4.33 + + + 6.64 + 4.71 + + + 6.64 + 5.09 + + + 6.78 + 5.44 + + + 7.0 + 5.75 + + + 7.01 + 6.13 + + + 6.95 + 6.51 + + + 6.7 + 6.80 + + + 6.33 + 6.89 + + + 5.95 + 6.91 + + + 7.02 + 4.38 + + + 7.0 + 4.76 + + + 6.99 + 5.15 + + + 15.66 + 4.98 + + + 15.66 + 5.36 + + + 15.66 + 5.74 + + + 15.67 + 6.12 + + + 15.67 + 6.50 + + + 15.68 + 6.88 + + + 15.88 + 7.21 + + + 16.1 + 7.52 + + + 16.21 + 7.88 + + + 16.25 + 8.26 + + + 16.24 + 8.64 + + + 16.25 + 9.02 + + + 16.25 + 9.40 + + + 15.87 + 9.45 + + + 15.87 + 9.08 + + + 15.87 + 8.70 + + + 15.87 + 8.32 + + + 15.86 + 7.94 + + + 15.74 + 7.58 + + + 15.54 + 7.26 + + + 15.36 + 6.93 + + + 15.29 + 6.55 + + + 15.3 + 6.17 + + + 15.31 + 5.80 + + + 15.3 + 5.42 + + + 15.28 + 5.04 + + + 16.25 + 9.90 + + + 16.52 + 10.17 + + + 16.81 + 10.42 + + + 16.72 + 10.79 + + + 16.46 + 11.07 + + + 16.11 + 11.22 + + + 15.74 + 11.13 + + + 15.45 + 10.88 + + + 15.4 + 10.51 + + + 15.53 + 10.15 + + + 15.8 + 9.89 + + + 16.32 + 11.43 + + + 16.28 + 11.81 + + + 16.26 + 12.19 + + + 16.28 + 12.57 + + + 16.48 + 12.89 + + + 16.83 + 13.02 + + + 17.21 + 13.03 + + + 17.59 + 13.02 + + + 17.97 + 13.02 + + + 18.35 + 13.02 + + + 18.73 + 13.02 + + + 19.11 + 13.02 + + + 19.49 + 13.00 + + + 19.78 + 12.76 + + + 19.94 + 12.42 + + + 19.94 + 12.04 + + + 19.94 + 11.66 + + + 19.94 + 11.28 + + + 19.78 + 10.93 + + + 19.43 + 10.77 + + + 19.06 + 10.74 + + + 18.67 + 10.74 + + + 18.29 + 10.74 + + + 17.91 + 10.73 + + + 17.53 + 10.75 + + + 17.15 + 10.77 + + + 17.18 + 10.39 + + + 17.56 + 10.38 + + + 17.94 + 10.38 + + + 18.32 + 10.39 + + + 18.7 + 10.40 + + + 19.08 + 10.39 + + + 19.46 + 10.40 + + + 19.82 + 10.50 + + + 20.1 + 10.75 + + + 20.28 + 11.09 + + + 20.36 + 11.46 + + + 20.34 + 11.84 + + + 20.31 + 12.22 + + + 20.24 + 12.60 + + + 20.12 + 12.96 + + + 19.88 + 13.25 + + + 19.52 + 13.39 + + + 19.14 + 13.44 + + + 18.76 + 13.45 + + + 18.38 + 13.45 + + + 18.0 + 13.44 + + + 17.62 + 13.45 + + + 17.24 + 13.45 + + + 16.86 + 13.42 + + + 16.49 + 13.32 + + + 16.2 + 13.08 + + + 16.01 + 12.75 + + + 15.9 + 12.39 + + + 15.88 + 12.01 + + + 15.88 + 11.63 + + + 15.08 + 10.79 + + + 14.7 + 10.77 + + + 14.32 + 10.77 + + + 13.94 + 10.76 + + + 13.94 + 10.35 + + + 14.32 + 10.36 + + + 14.7 + 10.35 + + + 15.08 + 10.35 + + + 0.74 + 10.95 + + + 0.79 + 11.33 + + + 0.98 + 11.66 + + + 1.34 + 11.77 + + + 1.72 + 11.78 + + + 2.1 + 11.78 + + + 2.48 + 11.78 + + + 2.86 + 11.78 + + + 3.24 + 11.78 + + + 3.62 + 11.78 + + + 4.0 + 11.78 + + + 4.37 + 11.78 + + + 4.75 + 11.79 + + + 5.13 + 11.79 + + + 5.51 + 11.78 + + + 5.89 + 11.78 + + + 6.27 + 11.78 + + + 6.65 + 11.77 + + + 6.7 + 12.16 + + + 6.32 + 12.16 + + + 5.94 + 12.16 + + + 5.56 + 12.15 + + + 5.18 + 12.15 + + + 4.8 + 12.15 + + + 4.42 + 12.16 + + + 4.04 + 12.16 + + + 3.66 + 12.16 + + + 3.27 + 12.15 + + + 2.89 + 12.15 + + + 2.51 + 12.15 + + + 2.13 + 12.15 + + + 1.75 + 12.16 + + + 1.37 + 12.15 + + + 1.0 + 12.07 + + + 0.68 + 11.86 + + + 0.47 + 11.54 + + + 0.34 + 11.19 + + + 0.33 + 10.81 + + + 13.72 + 11.07 + + + 13.35 + 11.18 + + + 12.97 + 11.18 + + + 12.59 + 11.18 + + + 12.21 + 11.19 + + + + + + + + + 11.83 + 11.24 + + + 11.47 + 11.36 + + + 11.11 + 11.49 + + + + + + + + + + 10.78 + 11.68 + + + 10.45 + 11.87 + + + 10.11 + 12.05 + + + 9.77 + 12.22 + + + 9.41 + 12.36 + + + 9.05 + 12.47 + + + 8.68 + 12.55 + + + 8.3 + 12.60 + + + 7.92 + 12.59 + + + 7.54 + 12.58 + + + 7.16 + 12.57 + + + 6.9 + 11.50 + + + 7.24 + 11.32 + + + 7.62 + 11.34 + + + 8.0 + 11.34 + + + 8.38 + 11.33 + + + 8.75 + 11.27 + + + 9.12 + 11.18 + + + 9.46 + 11.03 + + + 9.81 + 10.88 + + + 10.15 + 10.71 + + + 10.48 + 10.52 + + + 10.81 + 10.33 + + + 11.15 + 10.18 + + + 11.52 + 10.08 + + + 11.89 + 10.00 + + + 12.27 + 9.98 + + + 12.64 + 9.96 + + + 13.02 + 9.94 + + + 13.4 + 9.94 + + + 13.6 + 9.94 + + + 13.78 + 9.98 + + + 13.56 + 10.75 + + + 13.18 + 10.75 + + + 12.8 + 10.74 + + + 12.42 + 10.77 + + + 12.04 + 10.81 + + + 11.66 + 10.87 + + + 11.3 + 10.99 + + + 10.95 + 11.14 + + + 10.61 + 11.32 + + + 10.28 + 11.51 + + + 9.94 + 11.68 + + + 9.6 + 11.86 + + + 9.25 + 12.01 + + + 8.88 + 12.11 + + + 8.5 + 12.15 + + + 8.13 + 12.20 + + + 7.74 + 12.20 + + + 7.36 + 12.20 + + + 6.98 + 12.20 + + + 7.03 + 11.76 + + + 7.41 + 11.76 + + + 7.78 + 11.77 + + + 8.16 + 11.76 + + + 8.54 + 11.75 + + + 8.91 + 11.66 + + + 9.27 + 11.54 + + + 9.62 + 11.41 + + + 9.96 + 11.23 + + + 10.3 + 11.06 + + + 10.64 + 10.90 + + + 10.97 + 10.72 + + + 11.33 + 10.59 + + + 11.69 + 10.48 + + + 12.06 + 10.42 + + + 12.44 + 10.37 + + + 12.82 + 10.37 + + + 13.2 + 10.37 + + + 13.58 + 10.36 + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + True + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + False + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + False + + + False + + + False + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + + + + + + False + + + False + + + True + + + True + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + True + + + True + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + + + + + + + + + + + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + + + + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + + + + + + + True + + True + + True + True + True + + True + + True + True + True + + True + + True + True + True + + True + + True + True + True + + True + + True + True + True + + True + + True + True + True + + True + + True + True + True + + True + + True + True + True + + True + + True + True + True + + True + + True + + True + + + + + True + + True + + True + True + + + + + + True + + True + + True + True + + + + + + + + + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + + + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + + + \ No newline at end of file diff --git a/src/example/config/gercek2.graphml b/src/example/config/gercek2.graphml new file mode 100644 index 0000000..8228102 --- /dev/null +++ b/src/example/config/gercek2.graphml @@ -0,0 +1,3829 @@ + + + + + + + + + + + + + 9.34 + 0.54 + + + 10.04 + 0.54 + + + 10.99 + 0.54 + + + 11.71 + 0.54 + + + 12.41 + 0.54 + + + 9.34 + 1.3 + + + 10.03 + 1.3 + + + 10.73 + 1.3 + + + 11.41 + 1.3 + + + 13.11 + 1.3 + + + + + + 4.17 + 6.89 + + + 4.17 + 6.52 + + + 4.76 + 5.94 + + + 5.13 + 5.94 + + + 5.71 + 6.89 + + + 5.13 + 7.48 + + + 4.76 + 7.48 + + + 4.94 + 6.71 + + + 4.94 + 6.71 + + + 4.94 + 6.71 + + + 4.79 + 6.52 + + + 1.93 + 6.89 + + + 1.93 + 6.52 + + + 2.52 + 5.93 + + + 2.88 + 5.93 + + + 3.47 + 6.52 + + + 3.47 + 6.89 + + + 2.7 + 6.70 + + + 2.7 + 6.70 + + + 2.7 + 6.70 + + + 2.7 + 6.70 + + + 1.93 + 4.02 + + + 1.93 + 3.65 + + + 2.51 + 3.07 + + + 2.88 + 3.07 + + + 3.47 + 3.65 + + + 3.47 + 4.02 + + + 2.88 + 4.61 + + + 2.51 + 4.61 + + + 2.7 + 3.84 + + + 2.7 + 3.84 + + + 2.7 + 3.84 + + + 2.7 + 3.84 + + + 4.17 + 4.02 + + + 4.17 + 3.65 + + + 4.75 + 3.06 + + + 5.12 + 3.06 + + + 5.71 + 4.02 + + + 5.12 + 4.60 + + + 4.75 + 4.60 + + + 4.94 + 3.83 + + + 4.94 + 3.83 + + + 4.94 + 3.83 + + + 4.94 + 3.83 + + + 14.71 + 4.01 + + + 14.71 + 3.64 + + + 15.29 + 3.05 + + + 16.25 + 4.01 + + + 15.66 + 4.60 + + + 15.29 + 4.60 + + + 15.48 + 3.83 + + + 15.48 + 3.83 + + + 15.48 + 3.83 + + + 15.48 + 3.83 + + + 1.94 + 0.36 + + + 1.94 + 0.73 + + + 2.51 + 1.30 + + + 2.88 + 1.30 + + + 3.45 + 0.36 + + + 3.45 + 0.73 + + + 2.69 + 0.54 + + + 2.69 + 0.54 + + + 2.69 + 0.54 + + + 0.35 + 10.57 + + + 0.72 + 10.57 + + + 1.29 + 10.00 + + + 1.29 + 9.63 + + + 0.35 + 9.06 + + + 0.72 + 9.06 + + + 0.53 + 9.81 + + + 0.53 + 9.81 + + + 0.53 + 9.81 + + + 0.35 + 7.48 + + + 0.72 + 7.48 + + + 1.29 + 6.90 + + + 1.29 + 6.54 + + + 0.35 + 5.96 + + + 0.72 + 5.96 + + + 0.53 + 6.72 + + + 0.53 + 6.72 + + + 0.53 + 6.72 + + + 0.35 + 4.59 + + + 0.72 + 4.59 + + + 1.29 + 4.02 + + + 1.29 + 3.65 + + + 0.35 + 3.08 + + + 0.72 + 3.08 + + + 0.53 + 3.84 + + + 0.53 + 3.84 + + + 0.53 + 3.84 + + + 2.88 + 1.68 + + + 2.88 + 2.06 + + + 2.88 + 2.44 + + + 2.89 + 2.82 + + + 2.51 + 2.69 + + + 2.51 + 2.31 + + + 2.52 + 1.93 + + + 2.51 + 1.55 + + + 1.56 + 0.72 + + + 1.18 + 0.76 + + + 0.87 + 0.98 + + + 0.73 + 1.34 + + + 0.73 + 1.72 + + + 0.73 + 2.10 + + + 0.73 + 2.48 + + + 0.74 + 2.86 + + + 0.35 + 2.70 + + + 0.35 + 2.32 + + + 0.34 + 1.94 + + + 0.34 + 1.56 + + + 0.39 + 1.19 + + + 0.51 + 0.83 + + + 0.77 + 0.56 + + + 1.11 + 0.39 + + + 1.48 + 0.32 + + + 3.79 + 4.02 + + + 3.85 + 3.65 + + + 1.55 + 4.02 + + + 1.67 + 3.65 + + + 2.88 + 4.99 + + + 2.88 + 5.37 + + + 2.88 + 5.75 + + + 2.52 + 5.55 + + + 2.52 + 5.17 + + + 2.52 + 4.79 + + + 0.73 + 4.97 + + + 0.74 + 5.35 + + + 0.74 + 5.73 + + + 0.35 + 5.59 + + + 0.34 + 5.21 + + + 0.35 + 4.83 + + + 1.67 + 6.53 + + + 1.55 + 6.88 + + + 3.85 + 6.52 + + + 3.79 + 6.88 + + + 5.13 + 7.86 + + + 5.11 + 8.24 + + + 5.04 + 8.61 + + + 4.9 + 8.97 + + + 4.71 + 9.30 + + + 4.46 + 9.59 + + + 4.17 + 9.84 + + + 3.84 + 10.03 + + + 3.48 + 10.14 + + + 3.1 + 10.19 + + + 2.72 + 10.19 + + + 2.35 + 10.11 + + + 1.97 + 10.03 + + + 1.59 + 10.01 + + + 1.67 + 9.61 + + + 2.05 + 9.62 + + + 2.4 + 9.75 + + + 2.77 + 9.84 + + + 3.15 + 9.83 + + + 3.52 + 9.74 + + + 3.87 + 9.59 + + + 4.17 + 9.37 + + + 4.42 + 9.08 + + + 4.64 + 8.78 + + + 4.73 + 8.41 + + + 4.76 + 8.04 + + + 4.76 + 7.66 + + + 0.72 + 7.86 + + + 0.73 + 8.24 + + + 0.73 + 8.62 + + + 0.34 + 8.68 + + + 0.34 + 8.30 + + + 0.34 + 7.92 + + + 3.83 + 0.35 + + + 4.21 + 0.35 + + + 4.59 + 0.35 + + + 4.97 + 0.35 + + + 5.34 + 0.35 + + + 5.72 + 0.35 + + + 6.1 + 0.36 + + + 6.44 + 0.54 + + + 6.65 + 0.86 + + + 6.66 + 1.24 + + + 6.63 + 1.62 + + + 6.62 + 2.00 + + + 6.63 + 2.38 + + + 6.63 + 2.76 + + + 6.62 + 3.14 + + + 6.61 + 3.52 + + + 6.71 + 4.05 + + + 6.1 + 4.02 + + + 4.76 + 2.68 + + + 4.76 + 2.31 + + + 4.76 + 1.93 + + + 4.75 + 1.55 + + + 4.75 + 1.17 + + + 4.8 + 0.67 + + + 4.35 + 0.77 + + + 3.97 + 0.75 + + + 4.8 + 0.44 + + + 5.07 + 0.93 + + + 5.14 + 1.31 + + + 5.16 + 1.69 + + + 5.15 + 2.07 + + + 5.12 + 2.45 + + + 5.14 + 2.83 + + + 5.13 + 4.98 + + + 5.13 + 5.37 + + + 5.14 + 5.75 + + + 4.75 + 5.56 + + + 4.75 + 5.18 + + + 4.73 + 4.80 + + + 7.03 + 0.89 + + + 7.41 + 0.90 + + + 7.79 + 0.91 + + + 8.17 + 0.90 + + + 8.54 + 0.90 + + + 8.92 + 0.90 + + + 9.3 + 0.90 + + + 9.68 + 0.91 + + + 10.06 + 0.91 + + + 10.44 + 0.92 + + + 10.82 + 0.92 + + + 11.2 + 0.93 + + + 11.58 + 0.93 + + + 11.95 + 0.93 + + + 12.33 + 0.93 + + + 12.71 + 0.93 + + + 13.09 + 0.92 + + + 13.47 + 0.92 + + + 13.85 + 0.92 + + + 14.23 + 0.92 + + + 14.61 + 0.93 + + + 14.99 + 0.94 + + + 15.36 + 0.94 + + + 15.74 + 0.94 + + + 16.12 + 0.94 + + + 16.5 + 0.92 + + + 16.84 + 1.10 + + + 17.08 + 1.39 + + + 17.08 + 1.77 + + + 17.08 + 2.15 + + + 17.08 + 2.53 + + + 17.08 + 2.91 + + + 17.08 + 3.30 + + + 17.02 + 3.67 + + + 16.71 + 3.90 + + + 15.3 + 2.68 + + + 15.3 + 2.30 + + + 15.28 + 1.92 + + + 15.29 + 1.54 + + + 15.32 + 1.16 + + + 11.77 + 2.05 + + + 12.15 + 2.05 + + + 12.53 + 2.03 + + + 12.91 + 1.99 + + + 13.24 + 1.80 + + + 13.55 + 1.58 + + + 13.85 + 1.35 + + + 14.14 + 1.11 + + + 14.32 + 4.02 + + + 13.94 + 4.01 + + + 13.56 + 4.01 + + + 13.18 + 4.01 + + + 12.8 + 4.01 + + + 12.42 + 4.01 + + + 12.04 + 4.01 + + + 11.66 + 4.00 + + + 11.28 + 4.00 + + + 10.9 + 4.00 + + + 10.51 + 4.01 + + + 10.13 + 4.01 + + + 9.75 + 4.01 + + + 9.37 + 4.01 + + + 8.99 + 4.01 + + + 8.61 + 4.01 + + + 8.23 + 4.01 + + + 7.85 + 4.01 + + + 7.47 + 4.01 + + + 7.09 + 4.01 + + + 6.71 + 4.05 + + + 6.64 + 4.33 + + + 6.64 + 4.71 + + + 6.64 + 5.09 + + + 6.78 + 5.44 + + + 7.0 + 5.75 + + + 7.01 + 6.13 + + + 6.95 + 6.51 + + + 6.7 + 6.80 + + + 6.33 + 6.89 + + + 5.95 + 6.91 + + + 7.02 + 4.38 + + + 7.0 + 4.76 + + + 6.99 + 5.15 + + + 15.66 + 4.98 + + + 15.66 + 5.36 + + + 15.66 + 5.74 + + + 15.67 + 6.12 + + + 15.67 + 6.50 + + + 15.68 + 6.88 + + + 15.88 + 7.21 + + + 16.1 + 7.52 + + + 16.21 + 7.88 + + + 16.25 + 8.26 + + + 16.24 + 8.64 + + + 16.25 + 9.02 + + + 16.25 + 9.40 + + + 15.87 + 9.45 + + + 15.87 + 9.08 + + + 15.87 + 8.70 + + + 15.87 + 8.32 + + + 15.86 + 7.94 + + + 15.74 + 7.58 + + + 15.54 + 7.26 + + + 15.36 + 6.93 + + + 15.29 + 6.55 + + + 15.3 + 6.17 + + + 15.31 + 5.80 + + + 15.3 + 5.42 + + + 15.28 + 5.04 + + + 16.25 + 9.90 + + + 16.52 + 10.17 + + + 16.81 + 10.42 + + + 16.72 + 10.79 + + + 16.46 + 11.07 + + + 16.11 + 11.22 + + + 15.74 + 11.13 + + + 15.45 + 10.88 + + + 15.4 + 10.51 + + + 15.53 + 10.15 + + + 15.8 + 9.89 + + + 16.32 + 11.43 + + + 16.28 + 11.81 + + + 16.26 + 12.19 + + + 16.28 + 12.57 + + + 16.48 + 12.89 + + + 16.83 + 13.02 + + + 17.21 + 13.03 + + + 17.59 + 13.02 + + + 17.97 + 13.02 + + + 18.35 + 13.02 + + + 18.73 + 13.02 + + + 19.11 + 13.02 + + + 19.49 + 13.00 + + + 19.78 + 12.76 + + + 19.94 + 12.42 + + + 19.94 + 12.04 + + + 19.94 + 11.66 + + + 19.94 + 11.28 + + + 19.78 + 10.93 + + + 19.43 + 10.77 + + + 19.06 + 10.74 + + + 18.67 + 10.74 + + + 18.29 + 10.74 + + + 17.91 + 10.73 + + + 17.53 + 10.75 + + + 17.15 + 10.77 + + + 17.18 + 10.39 + + + 17.56 + 10.38 + + + 17.94 + 10.38 + + + 18.32 + 10.39 + + + 18.7 + 10.40 + + + 19.08 + 10.39 + + + 19.46 + 10.40 + + + 19.82 + 10.50 + + + 20.1 + 10.75 + + + 20.28 + 11.09 + + + 20.36 + 11.46 + + + 20.34 + 11.84 + + + 20.31 + 12.22 + + + 20.24 + 12.60 + + + 20.12 + 12.96 + + + 19.88 + 13.25 + + + 19.52 + 13.39 + + + 19.14 + 13.44 + + + 18.76 + 13.45 + + + 18.38 + 13.45 + + + 18.0 + 13.44 + + + 17.62 + 13.45 + + + 17.24 + 13.45 + + + 16.86 + 13.42 + + + 16.49 + 13.32 + + + 16.2 + 13.08 + + + 16.01 + 12.75 + + + 15.9 + 12.39 + + + 15.88 + 12.01 + + + 15.88 + 11.63 + + + 15.08 + 10.79 + + + 14.7 + 10.77 + + + 14.32 + 10.77 + + + 13.94 + 10.76 + + + 13.94 + 10.35 + + + 14.32 + 10.36 + + + 14.7 + 10.35 + + + 15.08 + 10.35 + + + 0.74 + 10.95 + + + 0.79 + 11.33 + + + 0.98 + 11.66 + + + 1.34 + 11.77 + + + 1.72 + 11.78 + + + 2.1 + 11.78 + + + 2.48 + 11.78 + + + 2.86 + 11.78 + + + 3.24 + 11.78 + + + 3.62 + 11.78 + + + 4.0 + 11.78 + + + 4.37 + 11.78 + + + 4.75 + 11.79 + + + 5.13 + 11.79 + + + 5.51 + 11.78 + + + 5.89 + 11.78 + + + 6.27 + 11.78 + + + 6.65 + 11.77 + + + 6.7 + 12.16 + + + 6.32 + 12.16 + + + 5.94 + 12.16 + + + 5.56 + 12.15 + + + 5.18 + 12.15 + + + 4.8 + 12.15 + + + 4.42 + 12.16 + + + 4.04 + 12.16 + + + 3.66 + 12.16 + + + 3.27 + 12.15 + + + 2.89 + 12.15 + + + 2.51 + 12.15 + + + 2.13 + 12.15 + + + 1.75 + 12.16 + + + 1.37 + 12.15 + + + 1.0 + 12.07 + + + 0.68 + 11.86 + + + 0.47 + 11.54 + + + 0.34 + 11.19 + + + 0.33 + 10.81 + + + 13.72 + 11.07 + + + 13.35 + 11.18 + + + 12.97 + 11.18 + + + 12.59 + 11.18 + + + 12.21 + 11.19 + + + 11.83 + 11.24 + + + 11.47 + 11.36 + + + 11.11 + 11.49 + + + 10.78 + 11.68 + + + 10.45 + 11.87 + + + 10.11 + 12.05 + + + 9.77 + 12.22 + + + 9.41 + 12.36 + + + 9.05 + 12.47 + + + 8.68 + 12.55 + + + 8.3 + 12.60 + + + 7.92 + 12.59 + + + 7.54 + 12.58 + + + 7.16 + 12.57 + + + 6.9 + 11.50 + + + 7.24 + 11.32 + + + 7.62 + 11.34 + + + 8.0 + 11.34 + + + 8.38 + 11.33 + + + 8.75 + 11.27 + + + 9.12 + 11.18 + + + 9.46 + 11.03 + + + 9.81 + 10.88 + + + 10.15 + 10.71 + + + 10.48 + 10.52 + + + 10.81 + 10.33 + + + 11.15 + 10.18 + + + 11.52 + 10.08 + + + 11.89 + 10.00 + + + 12.27 + 9.98 + + + 12.64 + 9.96 + + + 13.02 + 9.94 + + + 13.4 + 9.94 + + + 13.78 + 9.98 + + + 13.56 + 10.75 + + + 13.18 + 10.75 + + + 12.8 + 10.74 + + + 12.42 + 10.77 + + + 12.04 + 10.81 + + + 11.66 + 10.87 + + + 11.3 + 10.99 + + + 10.95 + 11.14 + + + 10.61 + 11.32 + + + 10.28 + 11.51 + + + 9.94 + 11.68 + + + 9.6 + 11.86 + + + 9.25 + 12.01 + + + 8.88 + 12.11 + + + 8.5 + 12.15 + + + 8.13 + 12.20 + + + 7.74 + 12.20 + + + 7.36 + 12.20 + + + 6.98 + 12.20 + + + 7.03 + 11.76 + + + 7.41 + 11.76 + + + 7.78 + 11.77 + + + 8.16 + 11.76 + + + 8.54 + 11.75 + + + 8.91 + 11.66 + + + 9.27 + 11.54 + + + 9.62 + 11.41 + + + 9.96 + 11.23 + + + 10.3 + 11.06 + + + 10.64 + 10.90 + + + 10.97 + 10.72 + + + 11.33 + 10.59 + + + 11.69 + 10.48 + + + 12.06 + 10.42 + + + 12.44 + 10.37 + + + 12.82 + 10.37 + + + 13.2 + 10.37 + + + 13.58 + 10.36 + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + True + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + False + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + False + + + False + + + False + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + + + + + + + False + + + False + + + True + + + True + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + True + + + True + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + + + + + + + + + + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + + True + + + \ No newline at end of file diff --git a/src/example/config/saved.rviz b/src/example/config/saved.rviz new file mode 100644 index 0000000..c075456 --- /dev/null +++ b/src/example/config/saved.rviz @@ -0,0 +1,330 @@ +Panels: + - Class: rviz/Displays + Help Height: 78 + Name: Displays + Property Tree Widget: + Expanded: + - /Global Options1 + - /Status1 + - /Odometry1 + - /Odometry1/Shape1 + - /Odometry1/Covariance1 + - /Odometry1/Covariance1/Position1 + - /Imu1/Box properties1 + - /Imu1/Acceleration properties1 + - /Imu2/Box properties1 + Splitter Ratio: 0.5 + Tree Height: 1079 + - Class: rviz/Selection + Name: Selection + - Class: rviz/Tool Properties + Expanded: + - /2D Pose Estimate1 + - /2D Nav Goal1 + - /Publish Point1 + Name: Tool Properties + Splitter Ratio: 0.5886790156364441 + - Class: rviz/Views + Expanded: + - /Current View1 + Name: Views + Splitter Ratio: 0.5 + - Class: rviz/Time + Name: Time + SyncMode: 0 + SyncSource: LaserScan +Preferences: + PromptSaveOnExit: true +Toolbars: + toolButtonStyle: 2 +Visualization Manager: + Class: "" + Displays: + - Alpha: 0.5 + Cell Size: 1 + Class: rviz/Grid + Color: 160; 160; 164 + Enabled: true + Line Style: + Line Width: 0.029999999329447746 + Value: Lines + Name: Grid + Normal Cell Count: 0 + Offset: + X: 0 + Y: 0 + Z: 0 + Plane: XY + Plane Cell Count: 10 + Reference Frame: + Value: true + - Alpha: 1 + Autocompute Intensity Bounds: true + Autocompute Value Bounds: + Max Value: 10 + Min Value: -10 + Value: true + Axis: Z + Channel Name: intensity + Class: rviz/LaserScan + Color: 255; 255; 255 + Color Transformer: Intensity + Decay Time: 0 + Enabled: true + Invert Rainbow: false + Max Color: 255; 255; 255 + Min Color: 0; 0; 0 + Name: LaserScan + Position Transformer: XYZ + Queue Size: 10 + Selectable: true + Size (Pixels): 3 + Size (m): 0.009999999776482582 + Style: Flat Squares + Topic: /zed2i/depth/scan + Unreliable: false + Use Fixed Frame: true + Use rainbow: true + Value: true + - Class: rviz/TF + Enabled: true + Filter (blacklist): "" + Filter (whitelist): "" + Frame Timeout: 15 + Frames: + All Enabled: true + base_link: + Value: true + bn055_link: + Value: true + map: + Value: true + odom: + Value: true + zed2i_base_link: + Value: true + zed2i_camera_center: + Value: true + zed2i_imu_link: + Value: true + zed2i_left_camera_frame: + Value: true + zed2i_left_camera_optical_frame: + Value: true + zed2i_right_camera_frame: + Value: true + zed2i_right_camera_optical_frame: + Value: true + Marker Alpha: 1 + Marker Scale: 1 + Name: TF + Show Arrows: true + Show Axes: true + Show Names: true + Tree: + map: + odom: + base_link: + bn055_link: + {} + zed2i_base_link: + zed2i_camera_center: + zed2i_left_camera_frame: + zed2i_imu_link: + {} + zed2i_left_camera_optical_frame: + {} + zed2i_right_camera_frame: + zed2i_right_camera_optical_frame: + {} + Update Interval: 0 + Value: true + - Angle Tolerance: 0.10000000149011612 + Class: rviz/Odometry + Covariance: + Orientation: + Alpha: 0.5 + Color: 255; 255; 127 + Color Style: Unique + Frame: Local + Offset: 1 + Scale: 1 + Value: false + Position: + Alpha: 0.10000000149011612 + Color: 204; 51; 204 + Scale: 0.5 + Value: true + Value: true + Enabled: true + Keep: 100 + Name: Odometry + Position Tolerance: 0.10000000149011612 + Queue Size: 10 + Shape: + Alpha: 0.10000000149011612 + Axes Length: 1 + Axes Radius: 0.10000000149011612 + Color: 255; 25; 0 + Head Length: 0.30000001192092896 + Head Radius: 0.10000000149011612 + Shaft Length: 1 + Shaft Radius: 0.05000000074505806 + Value: Arrow + Topic: /odometry/filtered + Unreliable: false + Value: true + - Acceleration properties: + Acc. vector alpha: 0.10000000149011612 + Acc. vector color: 255; 0; 0 + Acc. vector scale: 0.10000000149011612 + Derotate acceleration: true + Enable acceleration: false + Axes properties: + Axes scale: 1 + Enable axes: true + Box properties: + Box alpha: 1 + Box color: 255; 0; 0 + Enable box: true + x_scale: 0.30000001192092896 + y_scale: 0.10000000149011612 + z_scale: 0.10000000149011612 + Class: rviz_imu_plugin/Imu + Enabled: true + Name: Imu + Queue Size: 10 + Topic: /zed2i/zed_node/imu/data + Unreliable: false + Value: true + fixed_frame_orientation: true + - Acceleration properties: + Acc. vector alpha: 1 + Acc. vector color: 255; 0; 0 + Acc. vector scale: 1 + Derotate acceleration: true + Enable acceleration: false + Axes properties: + Axes scale: 1 + Enable axes: true + Box properties: + Box alpha: 1 + Box color: 114; 159; 207 + Enable box: true + x_scale: 0.30000001192092896 + y_scale: 0.10000000149011612 + z_scale: 0.10000000149011612 + Class: rviz_imu_plugin/Imu + Enabled: true + Name: Imu + Queue Size: 10 + Topic: /automobile/imu + Unreliable: false + Value: true + fixed_frame_orientation: true + - Alpha: 1 + Autocompute Intensity Bounds: true + Autocompute Value Bounds: + Max Value: 10 + Min Value: -10 + Value: true + Axis: Z + Channel Name: intensity + Class: rviz/PointCloud2 + Color: 255; 255; 255 + Color Transformer: RGB8 + Decay Time: 0 + Enabled: true + Invert Rainbow: false + Max Color: 255; 255; 255 + Min Color: 0; 0; 0 + Name: PointCloud2 + Position Transformer: XYZ + Queue Size: 10 + Selectable: true + Size (Pixels): 3 + Size (m): 0.009999999776482582 + Style: Flat Squares + Topic: /zed2i/zed_node/point_cloud/cloud_registered + Unreliable: false + Use Fixed Frame: true + Use rainbow: true + Value: true + - Circles color: 170; 0; 0 + Class: Obstacles + Enabled: true + Margin color: 0; 170; 0 + Name: Obstacles + Opacity: 0.75 + Queue Size: 10 + Segments color: 170; 170; 0 + Segments thickness: 0.029999999329447746 + Topic: /obstacles + Unreliable: false + Value: true + Enabled: true + Global Options: + Background Color: 48; 48; 48 + Default Light: true + Fixed Frame: map + Frame Rate: 30 + Name: root + Tools: + - Class: rviz/Interact + Hide Inactive Objects: true + - Class: rviz/MoveCamera + - Class: rviz/Select + - Class: rviz/FocusCamera + - Class: rviz/Measure + - Class: rviz/SetInitialPose + Theta std deviation: 0.2617993950843811 + Topic: /initialpose + X std deviation: 0.5 + Y std deviation: 0.5 + - Class: rviz/SetGoal + Topic: /move_base_simple/goal + - Class: rviz/PublishPoint + Single click: true + Topic: /clicked_point + Value: true + Views: + Current: + Class: rviz/Orbit + Distance: 2.729907751083374 + Enable Stereo Rendering: + Stereo Eye Separation: 0.05999999865889549 + Stereo Focal Distance: 1 + Swap Stereo Eyes: false + Value: false + Field of View: 0.7853981852531433 + Focal Point: + X: 5.912442684173584 + Y: 0.6095684170722961 + Z: -0.010561157017946243 + Focal Shape Fixed Size: true + Focal Shape Size: 0.05000000074505806 + Invert Z Axis: false + Name: Current View + Near Clip Distance: 0.009999999776482582 + Pitch: 0.5847973823547363 + Target Frame: + Yaw: 3.1035149097442627 + Saved: ~ +Window Geometry: + Displays: + collapsed: false + Height: 1376 + Hide Left Dock: false + Hide Right Dock: false + QMainWindow State: 000000ff00000000fd000000040000000000000156000004c2fc0200000008fb0000001200530065006c0065006300740069006f006e00000001e10000009b0000005c00fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000001ed000001df00000185000000a3fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afb000000100044006900730070006c006100790073010000003d000004c2000000c900fffffffb0000002000730065006c0065006300740069006f006e00200062007500660066006500720200000138000000aa0000023a00000294fb00000014005700690064006500530074006500720065006f02000000e6000000d2000003ee0000030bfb0000000c004b0069006e0065006300740200000186000001060000030c00000261000000010000010f000004c2fc0200000003fb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fb0000000a00560069006500770073010000003d000004c2000000a400fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e10000019700000003000009b80000003efc0100000002fb0000000800540069006d00650100000000000009b8000003bc00fffffffb0000000800540069006d0065010000000000000450000000000000000000000747000004c200000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000 + Selection: + collapsed: false + Time: + collapsed: false + Tool Properties: + collapsed: false + Views: + collapsed: false + Width: 2488 + X: 72 + Y: 27 diff --git a/src/example/graphml/Competition_track_graph.graphml b/src/example/graphml/Competition_track_graph.graphml new file mode 100644 index 0000000..b4fae57 --- /dev/null +++ b/src/example/graphml/Competition_track_graph.graphml @@ -0,0 +1,3685 @@ + + + + + + + + + 5.69 + 13.04 + + + 6.15 + 13.05 + + + + 6.44 + 12.63 + + + + 4.18 + 6.88 + + + 4.18 + 7.25 + + + 4.78 + 7.84 + + + 5.15 + 7.84 + + + 5.74 + 6.89 + + + 5.15 + 6.29 + + + 4.78 + 6.29 + + + 4.96 + 7.07 + + + 4.96 + 7.07 + + + 4.96 + 6.85 + + + 4.96 + 7.07 + + + 1.94 + 9.74 + + + 1.94 + 10.11 + + + 2.53 + 10.71 + + + 2.9 + 10.71 + + + 3.49 + 10.11 + + + 3.49 + 9.74 + + + 2.9 + 9.15 + + + 2.53 + 9.15 + + + 2.72 + 9.93 + + + 2.72 + 9.93 + + + 2.72 + 9.93 + + + 2.9 + 9.93 + + + 4.18 + 9.75 + + + 4.18 + 10.13 + + + 4.77 + 10.72 + + + 5.14 + 10.72 + + + 5.73 + 9.75 + + + 5.14 + 9.16 + + + 4.77 + 9.16 + + + 4.57 + 10.33 + + + 4.95 + 9.94 + + + 4.95 + 9.94 + + + 4.95 + 9.94 + + + 3.47 + 6.86 + + + 3.47 + 7.23 + + + 2.89 + 7.81 + + + 2.52 + 7.81 + + + 1.95 + 6.86 + + + 1.95 + 7.23 + + + 2.71 + 7.05 + + + 2.71 + 7.05 + + + 2.8 + 7.25 + + + 1.92 + 13.03 + + + 1.92 + 12.66 + + + 2.5 + 12.08 + + + 2.87 + 12.08 + + + 3.45 + 13.03 + + + 3.45 + 12.66 + + + 2.68 + 12.85 + + + 2.68 + 12.85 + + + 2.68 + 12.85 + + + 4.16 + 13.04 + + + 4.16 + 12.67 + + + 4.74 + 12.09 + + + 5.11 + 12.09 + + + 5.69 + 13.04 + + + 4.93 + 12.85 + + + 4.93 + 12.85 + + + 4.93 + 12.85 + + + 0.36 + 3.19 + + + 0.73 + 3.19 + + + 1.31 + 3.77 + + + 1.31 + 4.14 + + + 0.36 + 4.71 + + + 0.73 + 4.71 + + + 0.55 + 3.95 + + + 0.55 + 3.95 + + + 0.55 + 3.95 + + + 0.36 + 6.26 + + + 0.74 + 6.26 + + + 1.31 + 6.84 + + + 1.31 + 7.21 + + + 0.36 + 7.79 + + + 0.74 + 7.79 + + + 0.55 + 7.03 + + + 0.55 + 7.03 + + + 0.55 + 7.03 + + + 0.36 + 9.15 + + + 0.74 + 9.15 + + + 1.31 + 9.73 + + + 1.31 + 10.1 + + + 0.36 + 10.68 + + + 0.74 + 10.68 + + + 0.55 + 9.91 + + + 0.55 + 9.91 + + + 0.55 + 9.91 + + + 3.83 + 13.04 + + + 3.78 + 12.67 + + + 2.88 + 11.7 + + + 2.89 + 11.32 + + + 2.91 + 10.94 + + + 2.52 + 11.09 + + + 2.52 + 11.46 + + + 2.51 + 11.84 + + + 5.12 + 11.71 + + + 5.14 + 11.32 + + + 5.15 + 10.94 + + + 4.76 + 11.1 + + + 4.76 + 11.47 + + + 4.75 + 11.85 + + + 1.54 + 12.67 + + + 1.16 + 12.63 + + + 0.86 + 12.39 + + + 0.72 + 12.03 + + + 0.72 + 11.65 + + + 0.72 + 11.27 + + + 0.74 + 10.89 + + + 0.36 + 11.06 + + + 0.37 + 11.43 + + + 0.36 + 11.81 + + + 0.39 + 12.19 + + + 0.5 + 12.55 + + + 0.76 + 12.83 + + + 1.09 + 13.01 + + + 1.46 + 13.07 + + + 1.69 + 10.13 + + + 3.87 + 10.13 + + + 3.79 + 9.77 + + + 1.56 + 9.75 + + + 5.14 + 8.78 + + + 5.14 + 8.4 + + + 5.14 + 8.02 + + + 4.76 + 8.22 + + + 4.76 + 8.6 + + + 4.76 + 8.98 + + + 2.89 + 8.77 + + + 2.89 + 8.39 + + + 2.9 + 8.01 + + + 2.53 + 8.19 + + + 2.52 + 8.57 + + + 2.52 + 8.95 + + + 0.74 + 8.77 + + + 0.75 + 8.39 + + + 0.75 + 8.01 + + + 0.36 + 8.17 + + + 0.36 + 8.55 + + + 0.35 + 8.93 + + + 3.85 + 7.25 + + + 3.8 + 6.9 + + + 1.56 + 6.87 + + + 1.69 + 7.23 + + + 0.74 + 5.88 + + + 0.75 + 5.5 + + + 0.75 + 5.12 + + + 0.36 + 5.09 + + + 0.36 + 5.47 + + + 0.36 + 5.85 + + + 5.14 + 5.91 + + + 5.14 + 5.52 + + + 5.06 + 5.15 + + + 4.95 + 4.79 + + + 4.74 + 4.46 + + + 4.48 + 4.18 + + + 4.19 + 3.92 + + + 3.85 + 3.76 + + + 3.49 + 3.62 + + + 3.12 + 3.58 + + + 2.73 + 3.59 + + + 2.36 + 3.65 + + + 1.99 + 3.74 + + + 1.61 + 3.75 + + + 1.68 + 4.18 + + + 2.06 + 4.16 + + + 2.42 + 4.03 + + + 2.78 + 3.94 + + + 3.16 + 3.94 + + + 3.53 + 4.03 + + + 3.87 + 4.19 + + + 4.18 + 4.41 + + + 4.45 + 4.68 + + + 4.62 + 5.01 + + + 4.71 + 5.38 + + + 4.75 + 5.75 + + + 0.73 + 2.81 + + + 0.78 + 2.43 + + + 1.01 + 2.12 + + + 1.36 + 1.99 + + + 1.74 + 1.99 + + + 2.12 + 1.99 + + + 2.49 + 1.99 + + + 2.87 + 1.99 + + + 3.25 + 1.99 + + + 3.63 + 2.0 + + + 4.01 + 2.0 + + + 4.39 + 2.0 + + + 4.76 + 2.0 + + + 5.14 + 2.0 + + + 5.52 + 2.01 + + + 5.9 + 2.01 + + + 6.28 + 2.0 + + + 6.66 + 1.99 + + + 6.68 + 1.61 + + + 6.3 + 1.6 + + + 5.92 + 1.6 + + + 5.54 + 1.61 + + + 5.16 + 1.61 + + + 4.78 + 1.61 + + + 4.39 + 1.61 + + + 4.01 + 1.61 + + + 3.63 + 1.61 + + + 3.25 + 1.61 + + + 2.87 + 1.61 + + + 2.48 + 1.61 + + + 2.1 + 1.61 + + + 1.72 + 1.61 + + + 1.34 + 1.61 + + + 0.97 + 1.71 + + + 0.65 + 1.92 + + + 0.43 + 2.23 + + + 0.34 + 2.59 + + + 0.34 + 2.97 + + + 7.03 + 2.0 + + + 7.41 + 2.0 + + + 7.79 + 1.99 + + + 8.17 + 1.99 + + + 8.55 + 2.02 + + + 8.92 + 2.09 + + + 9.28 + 2.2 + + + 9.63 + 2.35 + + + 9.96 + 2.53 + + + 10.29 + 2.71 + + + 10.61 + 2.91 + + + 10.95 + 3.07 + + + 11.3 + 3.22 + + + 11.66 + 3.32 + + + 12.03 + 3.38 + + + 12.41 + 3.4 + + + 12.79 + 3.41 + + + 13.17 + 3.41 + + + 13.55 + 3.41 + + + 13.93 + 3.41 + + + 14.3 + 3.4 + + + 14.68 + 3.4 + + + 15.06 + 3.4 + + + 6.91 + 2.27 + + + 7.25 + 2.42 + + + 7.63 + 2.41 + + + 8.01 + 2.39 + + + 8.39 + 2.41 + + + 8.75 + 2.49 + + + 9.12 + 2.6 + + + 9.48 + 2.72 + + + 9.81 + 2.9 + + + 10.13 + 3.09 + + + 10.45 + 3.29 + + + 10.8 + 3.45 + + + 11.14 + 3.6 + + + 11.51 + 3.7 + + + 11.88 + 3.76 + + + 12.26 + 3.78 + + + 12.64 + 3.79 + + + 13.02 + 3.8 + + + 13.39 + 3.82 + + + 13.77 + 3.75 + + + 15.1 + 3.02 + + + 14.72 + 3.02 + + + 14.34 + 3.02 + + + 13.95 + 3.02 + + + 13.57 + 3.01 + + + 13.19 + 3.0 + + + 12.81 + 2.99 + + + 12.43 + 2.98 + + + 12.05 + 2.97 + + + 11.67 + 2.91 + + + 11.3 + 2.79 + + + 10.96 + 2.63 + + + 10.61 + 2.46 + + + 10.29 + 2.26 + + + 9.95 + 2.09 + + + 9.61 + 1.91 + + + 9.25 + 1.77 + + + 8.88 + 1.66 + + + 8.5 + 1.62 + + + 8.12 + 1.6 + + + 7.74 + 1.59 + + + 7.36 + 1.6 + + + 6.98 + 1.59 + + + 13.75 + 2.7 + + + 13.39 + 2.58 + + + 13.01 + 2.6 + + + 12.62 + 2.61 + + + 12.24 + 2.6 + + + 11.87 + 2.53 + + + 11.5 + 2.42 + + + 11.15 + 2.27 + + + 10.81 + 2.09 + + + 10.47 + 1.91 + + + 10.14 + 1.71 + + + 9.8 + 1.55 + + + 9.45 + 1.4 + + + 9.08 + 1.27 + + + 8.71 + 1.21 + + + 8.33 + 1.18 + + + 7.94 + 1.17 + + + 7.56 + 1.16 + + + 7.18 + 1.19 + + + 6.82 + 1.32 + + + 17.14 + 3.41 + + + 17.52 + 3.41 + + + 17.9 + 3.41 + + + 18.28 + 3.41 + + + 18.65 + 3.41 + + + 19.03 + 3.41 + + + 19.41 + 3.39 + + + 19.77 + 3.28 + + + 20.08 + 3.05 + + + 20.27 + 2.72 + + + 20.37 + 2.36 + + + 20.36 + 1.97 + + + 20.34 + 1.59 + + + 20.32 + 1.21 + + + 20.18 + 0.86 + + + 19.93 + 0.57 + + + 19.57 + 0.43 + + + 19.19 + 0.38 + + + 18.81 + 0.37 + + + 18.43 + 0.37 + + + 18.04 + 0.38 + + + 17.66 + 0.38 + + + 17.28 + 0.38 + + + 16.9 + 0.38 + + + 16.52 + 0.44 + + + 16.2 + 0.64 + + + 15.98 + 0.95 + + + 15.87 + 1.31 + + + 15.84 + 1.69 + + + 15.84 + 2.07 + + + 16.27 + 2.1 + + + 16.26 + 1.72 + + + 16.27 + 1.33 + + + 16.38 + 0.97 + + + 16.7 + 0.77 + + + 17.08 + 0.76 + + + 17.46 + 0.75 + + + 17.83 + 0.75 + + + 18.21 + 0.75 + + + 18.59 + 0.76 + + + 18.97 + 0.77 + + + 19.35 + 0.77 + + + 19.71 + 0.89 + + + 19.91 + 1.2 + + + 19.97 + 1.58 + + + 19.96 + 1.96 + + + 19.97 + 2.33 + + + 19.91 + 2.71 + + + 19.61 + 2.94 + + + 19.24 + 3.03 + + + 18.85 + 3.03 + + + 18.47 + 3.03 + + + 18.09 + 3.03 + + + 17.71 + 3.03 + + + 17.33 + 3.02 + + + 16.25 + 4.23 + + + 16.29 + 3.85 + + + 16.57 + 3.59 + + + 16.74 + 3.25 + + + 16.67 + 2.88 + + + 16.37 + 2.64 + + + 16.0 + 2.57 + + + 15.63 + 2.68 + + + 15.42 + 3.0 + + + 15.34 + 3.37 + + + 15.58 + 3.66 + + + 15.9 + 3.87 + + + 16.9 + 3.4 + + + 16.95 + 3.0 + + + 15.82 + 2.45 + + + 15.87 + 4.25 + + + 15.87 + 4.63 + + + 15.86 + 5.0 + + + 15.87 + 5.38 + + + 15.87 + 5.76 + + + 15.74 + 6.12 + + + 15.54 + 6.44 + + + 15.37 + 6.77 + + + 15.29 + 7.15 + + + 15.3 + 7.52 + + + 15.3 + 7.9 + + + 15.29 + 8.28 + + + 15.29 + 8.66 + + + 15.28 + 9.04 + + + 15.69 + 9.06 + + + 15.69 + 8.68 + + + 15.69 + 8.3 + + + 15.67 + 7.92 + + + 15.68 + 7.53 + + + 15.68 + 7.15 + + + 15.78 + 6.78 + + + 15.97 + 6.45 + + + 16.15 + 6.12 + + + 16.25 + 5.75 + + + 16.26 + 5.37 + + + 16.26 + 4.99 + + + 16.26 + 4.61 + + + 15.29 + 9.41 + + + 15.3 + 9.68 + + + 15.3 + 10.06 + + + 15.3 + 10.44 + + + 15.31 + 10.81 + + + 15.31 + 11.19 + + + 15.31 + 11.57 + + + 15.31 + 11.95 + + + 15.32 + 12.33 + + + 15.33 + 12.71 + + + 15.46 + 13.06 + + + 15.84 + 13.08 + + + 16.21 + 13.06 + + + 16.59 + 13.03 + + + 16.91 + 12.82 + + + 17.09 + 12.49 + + + 17.1 + 12.11 + + + 17.09 + 11.72 + + + 17.09 + 11.34 + + + 17.1 + 10.96 + + + 17.08 + 10.58 + + + 17.06 + 10.2 + + + 16.81 + 9.91 + + + 16.46 + 9.76 + + + 16.08 + 9.75 + + + 15.8 + 9.6 + + + 15.69 + 9.31 + + + 14.92 + 9.71 + + + 14.54 + 9.75 + + + 14.16 + 9.75 + + + 13.78 + 9.75 + + + 13.39 + 9.75 + + + 13.01 + 9.75 + + + 12.63 + 9.75 + + + 12.25 + 9.74 + + + 11.87 + 9.74 + + + 11.48 + 9.75 + + + 11.1 + 9.75 + + + 10.72 + 9.75 + + + 10.34 + 9.75 + + + 9.96 + 9.75 + + + 9.58 + 9.76 + + + 9.19 + 9.77 + + + 8.81 + 9.78 + + + 8.43 + 9.78 + + + 8.05 + 9.78 + + + 7.67 + 9.78 + + + 7.28 + 9.78 + + + 6.07 + 13.05 + + + 6.45 + 12.85 + + + 6.83 + 13.06 + + + 7.21 + 13.06 + + + 7.58 + 13.06 + + + 7.96 + 13.07 + + + 8.34 + 13.07 + + + 8.72 + 13.07 + + + 9.1 + 13.07 + + + 9.48 + 13.06 + + + 9.85 + 13.07 + + + 10.23 + 13.07 + + + 9.48 + 12.66 + + + 10.99 + 13.07 + + + 11.37 + 13.07 + + + 11.75 + 13.07 + + + 12.12 + 13.07 + + + 12.5 + 13.07 + + + 12.88 + 13.07 + + + 13.26 + 13.08 + + + 13.64 + 13.08 + + + 14.02 + 13.08 + + + 14.39 + 13.08 + + + 14.77 + 13.08 + + + 15.15 + 13.07 + + + 11.71 + 11.89 + + + 12.09 + 11.88 + + + 12.47 + 11.9 + + + 12.84 + 11.96 + + + 13.19 + 12.1 + + + 13.5 + 12.31 + + + 13.8 + 12.54 + + + 14.08 + 12.79 + + + + 6.65 + 12.33 + + + 6.65 + 11.95 + + + 6.64 + 11.57 + + + 6.63 + 11.19 + + + 6.63 + 10.8 + + + 6.63 + 10.42 + + + 6.61 + 10.04 + + + 6.6 + 9.76 + + + 6.22 + 9.76 + + + 6.6 + 9.38 + + + 6.61 + 8.99 + + + 6.62 + 8.61 + + + 6.81 + 8.28 + + + 7.01 + 7.96 + + + 6.9 + 9.78 + + + 7.06 + 9.47 + + + 7.03 + 9.09 + + + 7.02 + 8.7 + + + 7.04 + 8.32 + + + 7.0 + 7.58 + + + 6.92 + 7.2 + + + 6.64 + 6.95 + + + 6.26 + 6.9 + + + 6.6 + 9.76 + + + + + False + + + + False + + + False + + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + False + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + True + + + True + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + False + + + False + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + False + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + + + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + + False + + + False + + + + False + + + True + + + True + + + True + + + True + + + False + + + True + + + True + + + True + + + True + + + False + + + False + + + False + + + False + + + \ No newline at end of file diff --git a/src/example/graphml/Competition_track_graph.graphml:Zone.Identifier b/src/example/graphml/Competition_track_graph.graphml:Zone.Identifier new file mode 100644 index 0000000..053d112 --- /dev/null +++ b/src/example/graphml/Competition_track_graph.graphml:Zone.Identifier @@ -0,0 +1,3 @@ +[ZoneTransfer] +ZoneId=3 +HostUrl=about:internet diff --git a/src/example/graphml/fixed.graphml b/src/example/graphml/fixed.graphml new file mode 100644 index 0000000..d6a8a90 --- /dev/null +++ b/src/example/graphml/fixed.graphml @@ -0,0 +1,4229 @@ + + + + + + + + + + + + + 9.44 + 12.65 + + + 10.23 + 12.65 + + + 10.99 + 12.65 + + + 11.71 + 12.65 + + + 12.41 + 12.65 + + + 9.44 + 13.46 + + + 10.23 + 13.46 + + + 10.99 + 13.46 + + + 11.71 + 13.46 + + + 12.41 + 13.46 + + + + + 4.2 + 6.92 + + + 4.18 + 7.25 + + + 4.78 + 7.84 + + + 5.15 + 7.84 + + + 5.78 + 6.89 + + + 5.15 + 6.29 + + + 4.78 + 6.29 + + + 4.96 + 7.07 + + + 4.96 + 7.07 + + + 4.96 + 6.85 + + + 4.96 + 7.07 + + + 2.0 + 9.74 + + + 2.1 + 9.9 + + + 2.7 + 10.71 + + + 2.9 + 10.71 + + + 3.49 + 10.1 + + + 3.49 + 9.74 + + + 2.9 + 9.15 + + + 2.5 + 9.15 + + + 3.2 + 9.9 + + + 2.72 + 9.93 + + + 2.72 + 9.93 + + + 2.7 + 9.84 + + + 4.18 + 9.75 + + + 4.16 + 10.1 + + + 4.77 + 10.72 + + + 5.14 + 10.72 + + + 5.73 + 9.75 + + + 5.14 + 9.16 + + + 4.77 + 9.16 + + + 4.57 + 10.3 + + + 4.95 + 9.94 + + + 4.95 + 9.94 + + + 4.95 + 9.94 + + + 3.47 + 6.9 + + + 3.47 + 7.23 + + + 2.89 + 7.81 + + + 2.62 + 7.81 + + + 1.95 + 7.35 + + + 1.95 + 7.23 + + + 2.71 + 7.05 + + + 2.71 + 7.05 + + + 2.7 + 7.15 + + + 1.92 + 13.03 + + + 1.92 + 12.66 + + + 3.0 + 12.08 + + + 2.87 + 12.08 + + + 3.45 + 13.03 + + + 3.45 + 12.66 + + + 2.68 + 12.85 + + + 2.68 + 12.85 + + + 2.68 + 12.85 + + + 4.16 + 13.04 + + + 4.16 + 12.67 + + + 4.8 + 12.09 + + + 5.11 + 12.09 + + + 5.69 + 13.04 + + + 4.93 + 12.85 + + + 4.93 + 12.85 + + + 4.93 + 12.85 + + + 0.36 + 3.19 + + + 0.73 + 3.19 + + + 1.31 + 3.77 + + + 1.31 + 4.14 + + + 0.36 + 4.71 + + + 0.73 + 4.71 + + + 0.55 + 3.95 + + + 0.55 + 3.95 + + + 0.55 + 3.95 + + + 0.36 + 6.26 + + + 0.74 + 6.26 + + + 1.25 + 7.3 + + + 1.31 + 7.21 + + + 0.33 + 7.79 + + + 0.74 + 7.79 + + + 0.55 + 7.03 + + + 0.24 + 6.96 + + + 0.55 + 7.03 + + + 0.5 + 9.40 + + + 0.74 + 9.15 + + + 1.31 + 9.73 + + + 1.31 + 9.9 + + + 0.36 + 10.68 + + + 0.74 + 10.68 + + + 0.55 + 9.91 + + + 0.55 + 9.91 + + + 0.5 + 10.0 + + + 3.83 + 13.04 + + + 3.78 + 12.67 + + + 2.88 + 11.7 + + + 2.89 + 11.32 + + + 2.91 + 10.94 + + + 3.0 + 11.09 + + + 3.0 + 11.46 + + + 3.0 + 11.84 + + + 5.12 + 11.71 + + + 5.14 + 11.32 + + + 5.15 + 10.94 + + + 4.8 + 11.1 + + + 4.8 + 11.47 + + + 4.8 + 11.85 + + + 1.54 + 12.67 + + + 1.16 + 12.63 + + + 0.86 + 12.39 + + + 0.72 + 12.03 + + + 0.72 + 11.65 + + + 0.72 + 11.27 + + + 0.74 + 10.89 + + + 0.36 + 11.06 + + + 0.37 + 11.43 + + + 0.36 + 11.81 + + + 0.39 + 12.19 + + + 0.5 + 12.55 + + + 0.76 + 12.83 + + + 1.09 + 13.01 + + + 1.46 + 13.07 + + + 1.69 + 9.9 + + + 3.87 + 10.1 + + + 3.79 + 9.77 + + + 1.56 + 9.75 + + + 5.14 + 8.78 + + + 5.14 + 8.4 + + + 5.14 + 8.02 + + + 4.76 + 8.22 + + + 4.76 + 8.6 + + + 4.76 + 8.98 + + + 2.89 + 8.77 + + + 2.89 + 8.39 + + + 2.9 + 8.01 + + + 2.50 + 8.19 + + + 2.50 + 8.57 + + + 2.50 + 8.95 + + + 0.74 + 8.77 + + + 0.75 + 8.39 + + + 0.75 + 8.01 + + + 0.5 + 8.17 + + + 0.5 + 8.55 + + + 0.5 + 8.93 + + + 3.85 + 7.25 + + + 3.8 + 6.9 + + + 1.56 + 7.3 + + + 1.69 + 7.23 + + + 0.74 + 5.88 + + + 0.75 + 5.5 + + + 0.75 + 5.12 + + + 0.36 + 5.09 + + + 0.36 + 5.47 + + + 0.36 + 5.85 + + + 5.14 + 5.91 + + + 5.14 + 5.52 + + + 5.06 + 5.15 + + + 4.95 + 4.79 + + + 4.74 + 4.46 + + + 4.48 + 4.18 + + + 4.19 + 3.92 + + + 3.85 + 3.76 + + + 3.49 + 3.62 + + + 3.12 + 3.58 + + + 2.73 + 3.59 + + + 2.36 + 3.65 + + + 1.99 + 3.74 + + + 1.61 + 3.75 + + + 1.68 + 4.18 + + + 2.06 + 4.16 + + + 2.42 + 4.03 + + + 2.78 + 3.94 + + + 3.16 + 3.94 + + + 3.53 + 4.03 + + + 3.87 + 4.19 + + + 4.18 + 4.41 + + + 4.45 + 4.68 + + + 4.62 + 5.01 + + + 4.71 + 5.38 + + + 4.75 + 5.75 + + + 0.73 + 2.81 + + + 0.78 + 2.43 + + + 1.01 + 2.12 + + + 1.36 + 1.99 + + + 1.74 + 1.99 + + + 2.12 + 1.99 + + + 2.49 + 1.99 + + + 2.87 + 1.99 + + + 3.25 + 1.99 + + + 3.63 + 2.0 + + + 4.01 + 2.0 + + + 4.39 + 2.0 + + + 4.76 + 2.0 + + + 5.14 + 2.0 + + + 5.52 + 2.01 + + + 5.9 + 2.01 + + + 6.28 + 2.0 + + + 6.66 + 1.99 + + + 6.68 + 1.61 + + + 6.3 + 1.6 + + + 5.92 + 1.6 + + + 5.54 + 1.61 + + + 5.16 + 1.61 + + + 4.78 + 1.61 + + + 4.39 + 1.61 + + + 4.01 + 1.61 + + + 3.63 + 1.61 + + + 3.25 + 1.61 + + + 2.87 + 1.61 + + + 2.48 + 1.61 + + + 2.1 + 1.61 + + + 1.72 + 1.61 + + + 1.34 + 1.61 + + + 0.97 + 1.71 + + + 0.65 + 1.92 + + + 0.43 + 2.23 + + + 0.34 + 2.59 + + + 0.34 + 2.97 + + + 7.03 + 2.0 + + + 7.41 + 2.0 + + + 7.79 + 1.99 + + + 8.17 + 1.99 + + + 8.55 + 2.02 + + + 8.92 + 2.09 + + + 9.28 + 2.2 + + + 9.63 + 2.35 + + + 9.96 + 2.53 + + + 10.29 + 2.71 + + + 10.61 + 2.91 + + + 10.95 + 3.07 + + + 11.3 + 3.22 + + + 11.66 + 3.32 + + + 12.03 + 3.38 + + + 12.41 + 3.4 + + + 12.79 + 3.41 + + + 13.17 + 3.41 + + + 13.55 + 3.41 + + + 13.93 + 3.41 + + + 14.3 + 3.4 + + + 14.68 + 3.4 + + + 15.06 + 3.4 + + + 6.91 + 2.27 + + + 7.25 + 2.42 + + + 7.63 + 2.41 + + + 8.01 + 2.39 + + + 8.39 + 2.41 + + + 8.75 + 2.49 + + + 9.12 + 2.6 + + + 9.48 + 2.72 + + + 9.81 + 2.9 + + + 10.13 + 3.09 + + + 10.45 + 3.29 + + + 10.8 + 3.45 + + + 11.14 + 3.6 + + + 11.51 + 3.7 + + + 11.88 + 3.76 + + + 12.26 + 3.78 + + + 12.64 + 3.79 + + + 13.02 + 3.8 + + + 13.39 + 3.82 + + + 13.77 + 3.75 + + + 15.1 + 3.02 + + + 14.72 + 3.02 + + + 14.34 + 3.02 + + + 13.95 + 3.02 + + + 13.57 + 3.01 + + + 13.19 + 3.0 + + + 12.81 + 2.99 + + + 12.43 + 2.98 + + + 12.05 + 2.97 + + + 11.67 + 2.91 + + + 11.3 + 2.79 + + + 10.96 + 2.63 + + + 10.61 + 2.46 + + + 10.29 + 2.26 + + + 9.95 + 2.09 + + + 9.61 + 1.91 + + + 9.25 + 1.77 + + + 8.88 + 1.66 + + + 8.5 + 1.62 + + + 8.12 + 1.6 + + + 7.74 + 1.59 + + + 7.36 + 1.6 + + + 6.98 + 1.59 + + + 13.75 + 2.7 + + + 13.39 + 2.58 + + + 13.01 + 2.6 + + + 12.62 + 2.61 + + + 12.24 + 2.6 + + + 11.87 + 2.53 + + + 11.5 + 2.42 + + + 11.15 + 2.27 + + + 10.81 + 2.09 + + + 10.47 + 1.91 + + + 10.14 + 1.71 + + + 9.8 + 1.55 + + + 9.45 + 1.4 + + + 9.08 + 1.27 + + + 8.71 + 1.21 + + + 8.33 + 1.18 + + + 7.94 + 1.17 + + + 7.56 + 1.16 + + + 7.18 + 1.19 + + + 6.82 + 1.32 + + + 17.14 + 3.41 + + + 17.52 + 3.41 + + + 17.9 + 3.41 + + + 18.28 + 3.41 + + + 18.65 + 3.41 + + + 19.03 + 3.41 + + + 19.41 + 3.39 + + + 19.77 + 3.28 + + + 20.08 + 3.05 + + + 20.27 + 2.72 + + + 20.37 + 2.36 + + + 20.36 + 1.97 + + + 20.34 + 1.59 + + + 20.32 + 1.21 + + + 20.18 + 0.86 + + + 19.93 + 0.57 + + + 19.57 + 0.43 + + + 19.19 + 0.38 + + + 18.81 + 0.37 + + + 18.43 + 0.37 + + + 18.04 + 0.38 + + + 17.66 + 0.38 + + + 17.28 + 0.38 + + + 16.9 + 0.38 + + + 16.52 + 0.44 + + + 16.2 + 0.64 + + + 15.98 + 0.95 + + + 15.87 + 1.31 + + + 15.84 + 1.69 + + + 15.84 + 2.07 + + + 16.27 + 2.1 + + + 16.26 + 1.72 + + + 16.27 + 1.33 + + + 16.38 + 0.97 + + + 16.7 + 0.77 + + + 17.08 + 0.76 + + + 17.46 + 0.75 + + + 17.83 + 0.75 + + + 18.21 + 0.75 + + + 18.59 + 0.76 + + + 18.97 + 0.77 + + + 19.35 + 0.77 + + + 19.71 + 0.89 + + + 19.91 + 1.2 + + + 19.97 + 1.58 + + + 19.96 + 1.96 + + + 19.97 + 2.33 + + + 19.91 + 2.71 + + + 19.61 + 2.94 + + + 19.24 + 3.03 + + + 18.85 + 3.03 + + + 18.47 + 3.03 + + + 18.09 + 3.03 + + + 17.71 + 3.03 + + + 17.33 + 3.02 + + + 16.25 + 4.23 + + + 16.29 + 3.85 + + + 16.57 + 3.59 + + + 16.74 + 3.25 + + + 16.67 + 2.88 + + + 16.37 + 2.64 + + + 16.0 + 2.57 + + + 15.63 + 2.68 + + + 15.42 + 3.0 + + + 15.34 + 3.37 + + + 15.58 + 3.66 + + + 15.9 + 3.87 + + + 16.9 + 3.4 + + + 16.95 + 3.0 + + + 15.82 + 2.45 + + + 15.87 + 4.25 + + + 15.87 + 4.63 + + + 15.86 + 5.0 + + + 15.87 + 5.38 + + + 15.87 + 5.76 + + + 15.74 + 6.12 + + + 15.54 + 6.44 + + + 15.37 + 6.77 + + + 15.29 + 7.15 + + + 15.3 + 7.52 + + + 15.3 + 7.9 + + + 15.29 + 8.28 + + + 15.29 + 8.66 + + + 15.28 + 9.04 + + + 15.69 + 9.06 + + + 15.69 + 8.68 + + + 15.69 + 8.3 + + + 15.67 + 7.92 + + + 15.68 + 7.53 + + + 15.68 + 7.15 + + + 15.78 + 6.78 + + + 15.97 + 6.45 + + + 16.15 + 6.12 + + + 16.25 + 5.75 + + + 16.26 + 5.37 + + + 16.26 + 4.99 + + + 16.26 + 4.61 + + + 15.29 + 9.41 + + + 15.3 + 9.68 + + + 15.3 + 10.06 + + + 15.3 + 10.44 + + + 15.31 + 10.81 + + + 15.31 + 11.19 + + + 15.31 + 11.57 + + + 15.31 + 11.95 + + + 15.32 + 12.33 + + + 15.33 + 12.71 + + + 15.46 + 13.06 + + + 15.84 + 13.08 + + + 16.21 + 13.06 + + + 16.59 + 13.03 + + + 16.91 + 12.82 + + + 17.09 + 12.49 + + + 17.1 + 12.11 + + + 17.09 + 11.72 + + + 17.09 + 11.34 + + + 17.1 + 10.96 + + + 17.08 + 10.58 + + + 17.06 + 10.2 + + + 16.81 + 9.91 + + + 16.46 + 9.76 + + + 16.08 + 9.75 + + + 15.8 + 9.6 + + + 15.69 + 9.31 + + + 14.92 + 9.71 + + + 14.54 + 9.75 + + + 14.16 + 9.75 + + + 13.78 + 9.75 + + + 13.39 + 9.75 + + + 13.01 + 9.75 + + + 12.63 + 9.75 + + + 12.25 + 9.74 + + + 11.87 + 9.74 + + + 11.48 + 9.75 + + + 11.1 + 9.75 + + + 10.72 + 9.75 + + + 10.34 + 9.75 + + + 9.96 + 9.75 + + + 9.58 + 9.76 + + + 9.19 + 9.77 + + + 8.81 + 9.78 + + + 8.43 + 9.78 + + + 8.05 + 9.78 + + + 7.67 + 9.78 + + + 7.28 + 9.78 + + + 6.07 + 13.05 + + + 6.45 + 12.85 + + + 6.83 + 13.06 + + + 7.21 + 13.06 + + + 7.58 + 13.06 + + + 7.96 + 13.07 + + + 8.34 + 13.07 + + + 8.72 + 13.07 + + + 9.1 + 13.07 + + + 9.48 + 13.06 + + + 9.85 + 13.07 + + + 10.23 + 13.07 + + + 10.61 + 13.07 + + + 10.99 + 13.07 + + + 11.37 + 13.07 + + + 11.75 + 13.07 + + + 12.12 + 13.07 + + + 12.5 + 13.07 + + + 12.88 + 13.07 + + + 13.26 + 13.08 + + + 13.64 + 13.08 + + + 14.02 + 13.08 + + + 14.39 + 13.08 + + + 14.77 + 13.08 + + + 15.15 + 13.07 + + + 11.71 + 11.89 + + + 12.09 + 11.88 + + + 12.47 + 11.9 + + + 12.84 + 11.96 + + + 13.19 + 12.1 + + + 13.5 + 12.31 + + + 13.8 + 12.54 + + + 14.08 + 12.79 + + + 6.61 + 12.71 + + + 6.58 + 12.33 + + + 6.61 + 11.95 + + + 6.58 + 11.57 + + + 6.58 + 11.19 + + + 6.58 + 10.8 + + + 6.58 + 10.42 + + + 6.58 + 10.04 + + + 6.58 + 9.76 + + + 6.22 + 9.76 + + + 6.6 + 9.38 + + + 6.61 + 8.99 + + + 6.62 + 8.61 + + + 6.81 + 8.28 + + + 6.96 + 7.96 + + + 6.9 + 9.78 + + + 7.06 + 9.47 + + + 6.96 + 9.09 + + + 6.96 + 8.7 + + + 6.96 + 8.32 + + + 6.96 + 7.6 + + + 6.87 + 7.27 + + + 6.64 + 6.95 + + + 5.9 + 6.9 + + + 6.53 + 9.76 + + + + False + + + False + + + False + + + False + + + False + + + + False + + + False + + + + + False + + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + False + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + True + + + True + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + False + + + False + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + False + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + +True + + +True + +True +True + +True +True + +True +True + +True +True + +True +True + +True +True + +True +True + +True +True + +True +True + + + + + + +True +True +True +True +True +True +True +True +True +True +True +True +True +True +True +True +True +True +True + + + + + + + + +True + +True +True + +True +True + +True +True + +True +True + +True +True + +True +True + +True +True + +True +True + +True +True + +True +True + +True +True + +True +True + +True +True + +True +True + +True +True + +True +True + +True + + + + + +True + +True + +True + +True +True + +True + +True + +True +True + +True + +True + +True +True + +True + +True + +True +True + +True + +True + +True +True + +True + +True + +True +True + +True + +True + +True +True + +True + +True + +True +True + +True + +True + +True +True + +True + +True + +True +True + +True + +True + +True + + + + + + + + + + +True + +True + +True + +True +True + +True + +True + +True +True + +True + +True + +True +True + +True + +True + +True +True + +True + +True + +True +True + +True + +True + +True +True + +True + +True + +True +True + +True + +True + +True +True + +True + +True + +True + + +True + +True + +True +True +True + +True + +True +True +True + +True + +True +True +True + +True + +True +True +True + +True + +True +True +True + +True + +True +True +True + +True + +True +True +True + +True + +True +True +True + +True + +True +True +True + +True + +True +True +True + +True + +True +True +True + +True + +True +True +True + +True + +True +True +True + +True + +True +True +True + +True + +True +True +True + +True + +True +True +True + +True + +True +True +True + +True + +True +True + + + + +True + +True +True + + + + + + + + + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + + + + + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + True + + + True + + + + + + + + + + + + + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + + False + + + False + + + + False + + + True + + + True + + + True + + + True + + + False + + + True + + + True + + + True + + + True + + + False + + + False + + + False + + + False + + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + + \ No newline at end of file diff --git a/src/example/graphml/fixed2.graphml b/src/example/graphml/fixed2.graphml new file mode 100644 index 0000000..8c1620f --- /dev/null +++ b/src/example/graphml/fixed2.graphml @@ -0,0 +1,4068 @@ + + + + + + + + + + 9.34 + 0.54 + + + 10.04 + 0.54 + + + 10.99 + 0.54 + + + 11.71 + 0.54 + + + 12.41 + 0.54 + + + 9.34 + 1.3 + + + 10.03 + 1.3 + + + 10.73 + 1.3 + + + 11.41 + 1.3 + + + 13.11 + 1.3 + + + + + 4.17 + 6.89 + + + 4.17 + 6.52 + + + 4.76 + 5.94 + + + 5.13 + 5.94 + + + 5.71 + 6.89 + + + 5.13 + 7.48 + + + 4.76 + 7.48 + + + 4.94 + 6.71 + + + 4.94 + 6.71 + + + 4.94 + 6.71 + + + 4.79 + 6.52 + + + 1.93 + 6.89 + + + 1.93 + 6.52 + + + 2.52 + 5.93 + + + 2.88 + 5.93 + + + 3.47 + 6.52 + + + 3.47 + 6.89 + + + 2.7 + 6.70 + + + 2.7 + 6.70 + + + 2.7 + 6.70 + + + 2.7 + 6.70 + + + 1.93 + 4.02 + + + 1.93 + 3.65 + + + 2.51 + 3.07 + + + 2.88 + 3.07 + + + 3.47 + 3.65 + + + 3.47 + 4.02 + + + 2.88 + 4.61 + + + 2.51 + 4.61 + + + 2.7 + 3.84 + + + 2.7 + 3.84 + + + 2.7 + 3.84 + + + 2.7 + 3.84 + + + 4.17 + 4.02 + + + 4.17 + 3.65 + + + 4.75 + 3.06 + + + 5.12 + 3.06 + + + 5.71 + 4.02 + + + 5.12 + 4.60 + + + 4.75 + 4.60 + + + 4.94 + 3.83 + + + 4.94 + 3.83 + + + 4.94 + 3.83 + + + 4.94 + 3.83 + + + 14.71 + 4.01 + + + 14.71 + 3.64 + + + 15.29 + 3.05 + + + + + + + 16.25 + 4.01 + + + + + + + 15.66 + 4.60 + + + 15.29 + 4.60 + + + 15.48 + 3.83 + + + 15.48 + 3.83 + + + 15.48 + 3.83 + + + 15.48 + 3.83 + + + 1.94 + 0.36 + + + 1.94 + 0.73 + + + 2.51 + 1.30 + + + 2.88 + 1.30 + + + 3.45 + 0.36 + + + 3.45 + 0.73 + + + 2.69 + 0.54 + + + 2.69 + 0.54 + + + 2.69 + 0.54 + + + 0.35 + 10.57 + + + 0.72 + 10.57 + + + 1.29 + 10.00 + + + 1.29 + 9.63 + + + 0.35 + 9.06 + + + 0.72 + 9.06 + + + 0.53 + 9.81 + + + 0.53 + 9.81 + + + 0.53 + 9.81 + + + 0.35 + 7.48 + + + 0.72 + 7.48 + + + 1.29 + 6.90 + + + 1.29 + 6.54 + + + 0.35 + 5.96 + + + 0.72 + 5.96 + + + 0.53 + 6.72 + + + 0.53 + 6.72 + + + 0.53 + 6.72 + + + 0.35 + 4.59 + + + 0.72 + 4.59 + + + 1.29 + 4.02 + + + 1.29 + 3.65 + + + 0.35 + 3.08 + + + 0.72 + 3.08 + + + 0.53 + 3.84 + + + 0.53 + 3.84 + + + 0.53 + 3.84 + + + 2.88 + 1.68 + + + 2.88 + 2.06 + + + 2.88 + 2.44 + + + 2.89 + 2.82 + + + 2.51 + 2.69 + + + 2.51 + 2.31 + + + 2.52 + 1.93 + + + 2.51 + 1.55 + + + 1.56 + 0.72 + + + 1.18 + 0.76 + + + 0.87 + 0.98 + + + 0.73 + 1.34 + + + 0.73 + 1.72 + + + 0.73 + 2.10 + + + 0.73 + 2.48 + + + 0.74 + 2.86 + + + 0.35 + 2.70 + + + 0.35 + 2.32 + + + 0.34 + 1.94 + + + 0.34 + 1.56 + + + 0.39 + 1.19 + + + 0.51 + 0.83 + + + 0.77 + 0.56 + + + 1.11 + 0.39 + + + 1.48 + 0.32 + + + 3.79 + 4.02 + + + 3.85 + 3.65 + + + 1.55 + 4.02 + + + 1.67 + 3.65 + + + 2.88 + 4.99 + + + 2.88 + 5.37 + + + 2.88 + 5.75 + + + 2.52 + 5.55 + + + 2.52 + 5.17 + + + 2.52 + 4.79 + + + 0.73 + 4.97 + + + 0.74 + 5.35 + + + 0.74 + 5.73 + + + 0.35 + 5.59 + + + 0.34 + 5.21 + + + 0.35 + 4.83 + + + 1.67 + 6.53 + + + 1.55 + 6.88 + + + 3.85 + 6.52 + + + 3.79 + 6.88 + + + 5.13 + 7.86 + + + 5.11 + 8.24 + + + 5.04 + 8.61 + + + 4.9 + 8.97 + + + 4.71 + 9.30 + + + 4.46 + 9.59 + + + 4.17 + 9.84 + + + 3.84 + 10.03 + + + 3.48 + 10.14 + + + 3.1 + 10.19 + + + 2.72 + 10.19 + + + 2.35 + 10.11 + + + 1.97 + 10.03 + + + 1.59 + 10.01 + + + 1.67 + 9.61 + + + 2.05 + 9.62 + + + 2.4 + 9.75 + + + 2.77 + 9.84 + + + 3.15 + 9.83 + + + 3.52 + 9.74 + + + 3.87 + 9.59 + + + 4.17 + 9.37 + + + 4.42 + 9.08 + + + 4.64 + 8.78 + + + 4.73 + 8.41 + + + 4.76 + 8.04 + + + 4.76 + 7.66 + + + 0.72 + 7.86 + + + 0.73 + 8.24 + + + 0.73 + 8.62 + + + 0.34 + 8.68 + + + 0.34 + 8.30 + + + 0.34 + 7.92 + + + 3.83 + 0.35 + + + 4.21 + 0.35 + + + 4.59 + 0.35 + + + 4.97 + 0.35 + + + 5.34 + 0.35 + + + 5.72 + 0.35 + + + 6.1 + 0.36 + + + 6.44 + 0.54 + + + 6.65 + 0.86 + + + 6.66 + 1.24 + + + 6.63 + 1.62 + + + 6.62 + 2.00 + + + 6.63 + 2.38 + + + 6.63 + 2.76 + + + 6.62 + 3.14 + + + 6.61 + 3.52 + + + 6.71 + 4.05 + + + 6.1 + 4.02 + + + 4.76 + 2.68 + + + 4.76 + 2.31 + + + 4.76 + 1.93 + + + 4.75 + 1.55 + + + 4.75 + 1.17 + + + 4.8 + 0.67 + + + 4.35 + 0.77 + + + 3.97 + 0.75 + + + 4.8 + 0.44 + + + 5.07 + 0.93 + + + 5.14 + 1.31 + + + 5.16 + 1.69 + + + 5.15 + 2.07 + + + 5.12 + 2.45 + + + 5.14 + 2.83 + + + 5.13 + 4.98 + + + 5.13 + 5.37 + + + 5.14 + 5.75 + + + 4.75 + 5.56 + + + 4.75 + 5.18 + + + 4.73 + 4.80 + + + 7.03 + 0.89 + + + 7.41 + 0.90 + + + 7.79 + 0.91 + + + 8.17 + 0.90 + + + 8.54 + 0.90 + + + 8.92 + 0.90 + + + 9.3 + 0.90 + + + 9.68 + 0.91 + + + 10.06 + 0.91 + + + 10.44 + 0.92 + + + 10.82 + 0.92 + + + 11.2 + 0.93 + + + 11.58 + 0.93 + + + 11.95 + 0.93 + + + 12.33 + 0.93 + + + 12.71 + 0.93 + + + 13.09 + 0.92 + + + 13.47 + 0.92 + + + 13.85 + 0.92 + + + 14.23 + 0.92 + + + + + 14.61 + 0.93 + + + + + 14.99 + 0.94 + + + 15.36 + 0.94 + + + 15.74 + 0.94 + + + 16.12 + 0.94 + + + 16.5 + 0.92 + + + 16.84 + 1.10 + + + 17.08 + 1.39 + + + 17.08 + 1.77 + + + 17.08 + 2.15 + + + 17.08 + 2.53 + + + 17.08 + 2.91 + + + + + + + + 17.08 + 3.30 + + + 17.02 + 3.67 + + + 16.71 + 3.90 + + + 15.3 + 2.68 + + + 15.3 + 2.30 + + + 15.28 + 1.92 + + + 15.29 + 1.54 + + + 15.32 + 1.16 + + + 11.77 + 2.05 + + + 12.15 + 2.05 + + + 12.53 + 2.03 + + + 12.91 + 1.99 + + + 13.24 + 1.80 + + + 13.55 + 1.58 + + + 13.85 + 1.35 + + + 14.14 + 1.11 + + + 14.32 + 4.02 + + + 13.94 + 4.01 + + + 13.56 + 4.01 + + + 13.18 + 4.01 + + + 12.8 + 4.01 + + + 12.42 + 4.01 + + + 12.04 + 4.01 + + + 11.66 + 4.00 + + + 11.28 + 4.00 + + + 10.9 + 4.00 + + + 10.51 + 4.01 + + + 10.13 + 4.01 + + + 9.75 + 4.01 + + + 9.37 + 4.01 + + + 8.99 + 4.01 + + + 8.61 + 4.01 + + + 8.23 + 4.01 + + + 7.85 + 4.01 + + + 7.47 + 4.01 + + + 7.09 + 4.01 + + + 6.71 + 4.05 + + + 6.64 + 4.33 + + + 6.64 + 4.71 + + + 6.64 + 5.09 + + + 6.78 + 5.44 + + + 7.0 + 5.75 + + + 7.01 + 6.13 + + + 6.95 + 6.51 + + + 6.7 + 6.80 + + + 6.33 + 6.89 + + + 5.95 + 6.91 + + + 7.02 + 4.38 + + + 7.0 + 4.76 + + + 6.99 + 5.15 + + + 15.66 + 4.98 + + + 15.66 + 5.36 + + + 15.66 + 5.74 + + + 15.67 + 6.12 + + + 15.67 + 6.50 + + + 15.68 + 6.88 + + + 15.88 + 7.21 + + + 16.1 + 7.52 + + + 16.21 + 7.88 + + + 16.25 + 8.26 + + + 16.24 + 8.64 + + + 16.25 + 9.02 + + + 16.25 + 9.40 + + + 15.87 + 9.45 + + + 15.87 + 9.08 + + + 15.87 + 8.70 + + + 15.87 + 8.32 + + + 15.86 + 7.94 + + + 15.74 + 7.58 + + + 15.54 + 7.26 + + + 15.36 + 6.93 + + + 15.29 + 6.55 + + + 15.3 + 6.17 + + + 15.31 + 5.80 + + + 15.3 + 5.42 + + + 15.28 + 5.04 + + + 16.25 + 9.90 + + + 16.52 + 10.17 + + + 16.81 + 10.42 + + + 16.72 + 10.79 + + + 16.46 + 11.07 + + + 16.11 + 11.22 + + + 15.74 + 11.13 + + + 15.45 + 10.88 + + + 15.4 + 10.51 + + + 15.53 + 10.15 + + + 15.8 + 9.89 + + + 16.32 + 11.43 + + + 16.28 + 11.81 + + + 16.26 + 12.19 + + + 16.28 + 12.57 + + + 16.48 + 12.89 + + + 16.83 + 13.02 + + + 17.21 + 13.03 + + + 17.59 + 13.02 + + + 17.97 + 13.02 + + + 18.35 + 13.02 + + + 18.73 + 13.02 + + + 19.11 + 13.02 + + + 19.49 + 13.00 + + + 19.78 + 12.76 + + + 19.94 + 12.42 + + + 19.94 + 12.04 + + + 19.94 + 11.66 + + + 19.94 + 11.28 + + + 19.78 + 10.93 + + + 19.43 + 10.77 + + + 19.06 + 10.74 + + + 18.67 + 10.74 + + + 18.29 + 10.74 + + + 17.91 + 10.73 + + + 17.53 + 10.75 + + + 17.15 + 10.77 + + + 17.18 + 10.39 + + + 17.56 + 10.38 + + + 17.94 + 10.38 + + + 18.32 + 10.39 + + + 18.7 + 10.40 + + + 19.08 + 10.39 + + + 19.46 + 10.40 + + + 19.82 + 10.50 + + + 20.1 + 10.75 + + + 20.28 + 11.09 + + + 20.36 + 11.46 + + + 20.34 + 11.84 + + + 20.31 + 12.22 + + + 20.24 + 12.60 + + + 20.12 + 12.96 + + + 19.88 + 13.25 + + + 19.52 + 13.39 + + + 19.14 + 13.44 + + + 18.76 + 13.45 + + + 18.38 + 13.45 + + + 18.0 + 13.44 + + + 17.62 + 13.45 + + + 17.24 + 13.45 + + + 16.86 + 13.42 + + + 16.49 + 13.32 + + + 16.2 + 13.08 + + + 16.01 + 12.75 + + + 15.9 + 12.39 + + + 15.88 + 12.01 + + + 15.88 + 11.63 + + + 15.08 + 10.79 + + + 14.7 + 10.77 + + + 14.32 + 10.77 + + + 13.94 + 10.76 + + + 13.94 + 10.35 + + + 14.32 + 10.36 + + + 14.7 + 10.35 + + + 15.08 + 10.35 + + + 0.74 + 10.95 + + + 0.79 + 11.33 + + + 0.98 + 11.66 + + + 1.34 + 11.77 + + + 1.72 + 11.78 + + + 2.1 + 11.78 + + + 2.48 + 11.78 + + + 2.86 + 11.78 + + + 3.24 + 11.78 + + + 3.62 + 11.78 + + + 4.0 + 11.78 + + + 4.37 + 11.78 + + + 4.75 + 11.79 + + + 5.13 + 11.79 + + + 5.51 + 11.78 + + + 5.89 + 11.78 + + + 6.27 + 11.78 + + + 6.65 + 11.77 + + + 6.7 + 12.16 + + + 6.32 + 12.16 + + + 5.94 + 12.16 + + + 5.56 + 12.15 + + + 5.18 + 12.15 + + + 4.8 + 12.15 + + + 4.42 + 12.16 + + + 4.04 + 12.16 + + + 3.66 + 12.16 + + + 3.27 + 12.15 + + + 2.89 + 12.15 + + + 2.51 + 12.15 + + + 2.13 + 12.15 + + + 1.75 + 12.16 + + + 1.37 + 12.15 + + + 1.0 + 12.07 + + + 0.68 + 11.86 + + + 0.47 + 11.54 + + + 0.34 + 11.19 + + + 0.33 + 10.81 + + + 13.72 + 11.07 + + + 13.35 + 11.18 + + + 12.97 + 11.18 + + + 12.59 + 11.18 + + + 12.21 + 11.19 + + + + + + + + + 11.83 + 11.24 + + + 11.47 + 11.36 + + + 11.11 + 11.49 + + + + + + + + + + 10.78 + 11.68 + + + 10.45 + 11.87 + + + 10.11 + 12.05 + + + 9.77 + 12.22 + + + 9.41 + 12.36 + + + 9.05 + 12.47 + + + 8.68 + 12.55 + + + 8.3 + 12.60 + + + 7.92 + 12.59 + + + 7.54 + 12.58 + + + 7.16 + 12.57 + + + 6.9 + 11.50 + + + 7.24 + 11.32 + + + 7.62 + 11.34 + + + 8.0 + 11.34 + + + 8.38 + 11.33 + + + 8.75 + 11.27 + + + 9.12 + 11.18 + + + 9.46 + 11.03 + + + 9.81 + 10.88 + + + 10.15 + 10.71 + + + 10.48 + 10.52 + + + 10.81 + 10.33 + + + 11.15 + 10.18 + + + 11.52 + 10.08 + + + 11.89 + 10.00 + + + 12.27 + 9.98 + + + 12.64 + 9.96 + + + 13.02 + 9.94 + + + 13.4 + 9.94 + + + 13.6 + 9.94 + + + 13.78 + 9.98 + + + 13.56 + 10.75 + + + 13.18 + 10.75 + + + 12.8 + 10.74 + + + 12.42 + 10.77 + + + 12.04 + 10.81 + + + 11.66 + 10.87 + + + 11.3 + 10.99 + + + 10.95 + 11.14 + + + 10.61 + 11.32 + + + 10.28 + 11.51 + + + 9.94 + 11.68 + + + 9.6 + 11.86 + + + 9.25 + 12.01 + + + 8.88 + 12.11 + + + 8.5 + 12.15 + + + 8.13 + 12.20 + + + 7.74 + 12.20 + + + 7.36 + 12.20 + + + 6.98 + 12.20 + + + 7.03 + 11.76 + + + 7.41 + 11.76 + + + 7.78 + 11.77 + + + 8.16 + 11.76 + + + 8.54 + 11.75 + + + 8.91 + 11.66 + + + 9.27 + 11.54 + + + 9.62 + 11.41 + + + 9.96 + 11.23 + + + 10.3 + 11.06 + + + 10.64 + 10.90 + + + 10.97 + 10.72 + + + 11.33 + 10.59 + + + 11.69 + 10.48 + + + 12.06 + 10.42 + + + 12.44 + 10.37 + + + 12.82 + 10.37 + + + 13.2 + 10.37 + + + 13.58 + 10.36 + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + True + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + False + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + False + + + False + + + False + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + + + + + + False + + + False + + + True + + + True + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + True + + + True + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + + + + + + + + + + + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + + + + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + + + + + + + True + + True + + True + True + True + + True + + True + True + True + + True + + True + True + True + + True + + True + True + True + + True + + True + True + True + + True + + True + True + True + + True + + True + True + True + + True + + True + True + True + + True + + True + True + True + + True + + True + + True + + + + + True + + True + + True + True + + + + + + True + + True + + True + True + + + + + + + + + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + + + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + + + \ No newline at end of file diff --git a/src/example/graphml/gercek.graphml b/src/example/graphml/gercek.graphml new file mode 100644 index 0000000..14072e6 --- /dev/null +++ b/src/example/graphml/gercek.graphml @@ -0,0 +1,3788 @@ + + + + + + + + + + + + + 9.44 + 12.65 + + + 10.23 + 12.65 + + + 10.99 + 12.65 + + + 11.71 + 12.65 + + + 12.41 + 12.65 + + + 9.44 + 13.46 + + + 10.23 + 13.46 + + + 10.99 + 13.46 + + + 11.71 + 13.46 + + + 12.41 + 13.46 + + + + + 4.2 + 6.92 + + + 4.18 + 7.25 + + + 4.78 + 7.84 + + + 5.15 + 7.84 + + + 5.78 + 6.89 + + + 5.15 + 6.29 + + + 4.78 + 6.29 + + + 4.96 + 7.07 + + + 4.96 + 7.07 + + + 4.96 + 6.85 + + + 4.96 + 7.07 + + + 2.0 + 9.74 + + + 2.1 + 9.9 + + + 2.7 + 10.71 + + + 2.9 + 10.71 + + + 3.49 + 10.15 + + + 3.49 + 9.74 + + + 2.9 + 9.15 + + + 2.5 + 9.15 + + + 3.2 + 9.9 + + + 2.72 + 9.93 + + + 2.72 + 9.93 + + + 2.7 + 9.84 + + + 4.18 + 9.75 + + + 4.16 + 10.0 + + + 4.77 + 10.72 + + + 5.14 + 10.72 + + + 5.73 + 9.75 + + + 5.14 + 9.16 + + + 4.77 + 9.16 + + + 4.57 + 10.3 + + + 4.95 + 9.94 + + + 4.95 + 9.94 + + + 4.95 + 9.94 + + + 3.47 + 6.9 + + + 3.47 + 7.23 + + + 2.89 + 7.81 + + + 2.62 + 7.81 + + + 1.95 + 7.35 + + + 1.95 + 7.23 + + + 2.71 + 7.05 + + + 2.71 + 7.05 + + + 2.7 + 7.15 + + + 1.92 + 13.03 + + + 1.92 + 12.66 + + + 3.0 + 12.08 + + + 2.87 + 12.08 + + + 3.45 + 13.03 + + + 3.45 + 12.66 + + + 2.68 + 12.85 + + + 2.68 + 12.85 + + + 2.68 + 12.85 + + + 4.16 + 13.04 + + + 4.16 + 12.67 + + + 4.85 + 12.09 + + + 5.11 + 12.09 + + + 5.69 + 13.04 + + + 4.93 + 12.85 + + + 4.93 + 12.85 + + + 4.93 + 12.85 + + + 0.36 + 3.19 + + + 0.73 + 3.19 + + + 1.31 + 3.77 + + + 1.31 + 4.14 + + + 0.36 + 4.71 + + + 0.73 + 4.71 + + + 0.55 + 3.95 + + + 0.55 + 3.95 + + + 0.55 + 3.95 + + + 0.36 + 6.26 + + + 0.74 + 6.26 + + + 1.25 + 7.3 + + + 1.31 + 7.21 + + + 0.33 + 7.79 + + + 0.74 + 7.79 + + + 0.55 + 7.03 + + + 0.24 + 6.96 + + + 0.55 + 7.03 + + + 0.5 + 9.40 + + + 0.74 + 9.15 + + + 1.31 + 9.73 + + + 1.31 + 9.9 + + + 0.36 + 10.68 + + + 0.74 + 10.68 + + + 0.55 + 9.91 + + + 0.55 + 9.91 + + + 0.5 + 10.0 + + + 3.83 + 13.04 + + + 3.78 + 12.67 + + + 2.88 + 11.7 + + + 2.89 + 11.32 + + + 2.91 + 10.94 + + + 3.0 + 11.09 + + + 3.0 + 11.46 + + + 3.0 + 11.84 + + + 5.12 + 11.71 + + + 5.14 + 11.32 + + + 5.15 + 10.94 + + + 4.85 + 11.1 + + + 4.85 + 11.47 + + + 4.85 + 11.85 + + + 1.54 + 12.67 + + + 1.16 + 12.63 + + + 0.86 + 12.39 + + + 0.72 + 12.03 + + + 0.72 + 11.65 + + + 0.72 + 11.27 + + + 0.74 + 10.89 + + + 0.36 + 11.06 + + + 0.37 + 11.43 + + + 0.36 + 11.81 + + + 0.39 + 12.19 + + + 0.5 + 12.55 + + + 0.76 + 12.83 + + + 1.09 + 13.01 + + + 1.46 + 13.07 + + + 1.69 + 9.9 + + + 3.87 + 10.0 + + + 3.79 + 9.77 + + + 1.56 + 9.75 + + + 5.14 + 8.78 + + + 5.14 + 8.4 + + + 5.14 + 8.02 + + + 4.76 + 8.22 + + + 4.76 + 8.6 + + + 4.76 + 8.98 + + + 2.89 + 8.77 + + + 2.89 + 8.39 + + + 2.9 + 8.01 + + + 2.50 + 8.19 + + + 2.50 + 8.57 + + + 2.50 + 8.95 + + + 0.74 + 8.77 + + + 0.75 + 8.39 + + + 0.75 + 8.01 + + + 0.5 + 8.17 + + + 0.5 + 8.55 + + + 0.5 + 8.93 + + + 3.85 + 7.25 + + + 3.8 + 6.9 + + + 1.56 + 7.3 + + + 1.69 + 7.23 + + + 0.74 + 5.88 + + + 0.75 + 5.5 + + + 0.75 + 5.12 + + + 0.36 + 5.09 + + + 0.36 + 5.47 + + + 0.36 + 5.85 + + + 5.14 + 5.91 + + + 5.14 + 5.52 + + + 5.06 + 5.15 + + + 4.95 + 4.79 + + + 4.74 + 4.46 + + + 4.48 + 4.18 + + + 4.19 + 3.92 + + + 3.85 + 3.76 + + + 3.49 + 3.62 + + + 3.12 + 3.58 + + + 2.73 + 3.59 + + + 2.36 + 3.65 + + + 1.99 + 3.74 + + + 1.61 + 3.75 + + + 1.68 + 4.18 + + + 2.06 + 4.16 + + + 2.42 + 4.03 + + + 2.78 + 3.94 + + + 3.16 + 3.94 + + + 3.53 + 4.03 + + + 3.87 + 4.19 + + + 4.18 + 4.41 + + + 4.45 + 4.68 + + + 4.62 + 5.01 + + + 4.71 + 5.38 + + + 4.75 + 5.75 + + + 0.73 + 2.81 + + + 0.78 + 2.43 + + + 1.01 + 2.12 + + + 1.36 + 1.99 + + + 1.74 + 1.99 + + + 2.12 + 1.99 + + + 2.49 + 1.99 + + + 2.87 + 1.99 + + + 3.25 + 1.99 + + + 3.63 + 2.0 + + + 4.01 + 2.0 + + + 4.39 + 2.0 + + + 4.76 + 2.0 + + + 5.14 + 2.0 + + + 5.52 + 2.01 + + + 5.9 + 2.01 + + + 6.28 + 2.0 + + + 6.66 + 1.99 + + + 6.68 + 1.61 + + + 6.3 + 1.6 + + + 5.92 + 1.6 + + + 5.54 + 1.61 + + + 5.16 + 1.61 + + + 4.78 + 1.61 + + + 4.39 + 1.61 + + + 4.01 + 1.61 + + + 3.63 + 1.61 + + + 3.25 + 1.61 + + + 2.87 + 1.61 + + + 2.48 + 1.61 + + + 2.1 + 1.61 + + + 1.72 + 1.61 + + + 1.34 + 1.61 + + + 0.97 + 1.71 + + + 0.65 + 1.92 + + + 0.43 + 2.23 + + + 0.34 + 2.59 + + + 0.34 + 2.97 + + + 7.03 + 2.0 + + + 7.41 + 2.0 + + + 7.79 + 1.99 + + + 8.17 + 1.99 + + + 8.55 + 2.02 + + + 8.92 + 2.09 + + + 9.28 + 2.2 + + + 9.63 + 2.35 + + + 9.96 + 2.53 + + + 10.29 + 2.71 + + + 10.61 + 2.91 + + + 10.95 + 3.07 + + + 11.3 + 3.22 + + + 11.66 + 3.32 + + + 12.03 + 3.38 + + + 12.41 + 3.4 + + + 12.79 + 3.41 + + + 13.17 + 3.41 + + + 13.55 + 3.41 + + + 13.93 + 3.41 + + + 14.3 + 3.4 + + + 14.68 + 3.4 + + + 15.06 + 3.4 + + + 6.91 + 2.27 + + + 7.25 + 2.42 + + + 7.63 + 2.41 + + + 8.01 + 2.39 + + + 8.39 + 2.41 + + + 8.75 + 2.49 + + + 9.12 + 2.6 + + + 9.48 + 2.72 + + + 9.81 + 2.9 + + + 10.13 + 3.09 + + + 10.45 + 3.29 + + + 10.8 + 3.45 + + + 11.14 + 3.6 + + + 11.51 + 3.7 + + + 11.88 + 3.76 + + + 12.26 + 3.78 + + + 12.64 + 3.79 + + + 13.02 + 3.8 + + + 13.39 + 3.82 + + + 13.77 + 3.75 + + + 15.1 + 3.02 + + + 14.72 + 3.02 + + + 14.34 + 3.02 + + + 13.95 + 3.02 + + + 13.57 + 3.01 + + + 13.19 + 3.0 + + + 12.81 + 2.99 + + + 12.43 + 2.98 + + + 12.05 + 2.97 + + + 11.67 + 2.91 + + + 11.3 + 2.79 + + + 10.96 + 2.63 + + + 10.61 + 2.46 + + + 10.29 + 2.26 + + + 9.95 + 2.09 + + + 9.61 + 1.91 + + + 9.25 + 1.77 + + + 8.88 + 1.66 + + + 8.5 + 1.62 + + + 8.12 + 1.6 + + + 7.74 + 1.59 + + + 7.36 + 1.6 + + + 6.98 + 1.59 + + + 13.75 + 2.7 + + + 13.39 + 2.58 + + + 13.01 + 2.6 + + + 12.62 + 2.61 + + + 12.24 + 2.6 + + + 11.87 + 2.53 + + + 11.5 + 2.42 + + + 11.15 + 2.27 + + + 10.81 + 2.09 + + + 10.47 + 1.91 + + + 10.14 + 1.71 + + + 9.8 + 1.55 + + + 9.45 + 1.4 + + + 9.08 + 1.27 + + + 8.71 + 1.21 + + + 8.33 + 1.18 + + + 7.94 + 1.17 + + + 7.56 + 1.16 + + + 7.18 + 1.19 + + + 6.82 + 1.32 + + + 17.14 + 3.41 + + + 17.52 + 3.41 + + + 17.9 + 3.41 + + + 18.28 + 3.41 + + + 18.65 + 3.41 + + + 19.03 + 3.41 + + + 19.41 + 3.39 + + + 19.77 + 3.28 + + + 20.08 + 3.05 + + + 20.27 + 2.72 + + + 20.37 + 2.36 + + + 20.36 + 1.97 + + + 20.34 + 1.59 + + + 20.32 + 1.21 + + + 20.18 + 0.86 + + + 19.93 + 0.57 + + + 19.57 + 0.43 + + + 19.19 + 0.38 + + + 18.81 + 0.37 + + + 18.43 + 0.37 + + + 18.04 + 0.38 + + + 17.66 + 0.38 + + + 17.28 + 0.38 + + + 16.9 + 0.38 + + + 16.52 + 0.44 + + + 16.2 + 0.64 + + + 15.98 + 0.95 + + + 15.87 + 1.31 + + + 15.84 + 1.69 + + + 15.84 + 2.07 + + + 16.27 + 2.1 + + + 16.26 + 1.72 + + + 16.27 + 1.33 + + + 16.38 + 0.97 + + + 16.7 + 0.77 + + + 17.08 + 0.76 + + + 17.46 + 0.75 + + + 17.83 + 0.75 + + + 18.21 + 0.75 + + + 18.59 + 0.76 + + + 18.97 + 0.77 + + + 19.35 + 0.77 + + + 19.71 + 0.89 + + + 19.91 + 1.2 + + + 19.97 + 1.58 + + + 19.96 + 1.96 + + + 19.97 + 2.33 + + + 19.91 + 2.71 + + + 19.61 + 2.94 + + + 19.24 + 3.03 + + + 18.85 + 3.03 + + + 18.47 + 3.03 + + + 18.09 + 3.03 + + + 17.71 + 3.03 + + + 17.33 + 3.02 + + + 16.25 + 4.23 + + + 16.29 + 3.85 + + + 16.57 + 3.59 + + + 16.74 + 3.25 + + + 16.67 + 2.88 + + + 16.37 + 2.64 + + + 16.0 + 2.57 + + + 15.63 + 2.68 + + + 15.42 + 3.0 + + + 15.34 + 3.37 + + + 15.58 + 3.66 + + + 15.9 + 3.87 + + + 16.9 + 3.4 + + + 16.95 + 3.0 + + + 15.82 + 2.45 + + + 15.87 + 4.25 + + + 15.87 + 4.63 + + + 15.86 + 5.0 + + + 15.87 + 5.38 + + + 15.87 + 5.76 + + + 15.74 + 6.12 + + + 15.54 + 6.44 + + + 15.37 + 6.77 + + + 15.29 + 7.15 + + + 15.3 + 7.52 + + + 15.3 + 7.9 + + + 15.29 + 8.28 + + + 15.29 + 8.66 + + + 15.28 + 9.04 + + + 15.69 + 9.06 + + + 15.69 + 8.68 + + + 15.69 + 8.3 + + + 15.67 + 7.92 + + + 15.68 + 7.53 + + + 15.68 + 7.15 + + + 15.78 + 6.78 + + + 15.97 + 6.45 + + + 16.15 + 6.12 + + + 16.25 + 5.75 + + + 16.26 + 5.37 + + + 16.26 + 4.99 + + + 16.26 + 4.61 + + + 15.29 + 9.41 + + + 15.3 + 9.68 + + + 15.3 + 10.06 + + + 15.3 + 10.44 + + + 15.31 + 10.81 + + + 15.31 + 11.19 + + + 15.31 + 11.57 + + + 15.31 + 11.95 + + + 15.32 + 12.33 + + + 15.33 + 12.71 + + + 15.46 + 13.06 + + + 15.84 + 13.08 + + + 16.21 + 13.06 + + + 16.59 + 13.03 + + + 16.91 + 12.82 + + + 17.09 + 12.49 + + + 17.1 + 12.11 + + + 17.09 + 11.72 + + + 17.09 + 11.34 + + + 17.1 + 10.96 + + + 17.08 + 10.58 + + + 17.06 + 10.2 + + + 16.81 + 9.91 + + + 16.46 + 9.76 + + + 16.08 + 9.75 + + + 15.8 + 9.6 + + + 15.69 + 9.31 + + + 14.92 + 9.71 + + + 14.54 + 9.75 + + + 14.16 + 9.75 + + + 13.78 + 9.75 + + + 13.39 + 9.75 + + + 13.01 + 9.75 + + + 12.63 + 9.75 + + + 12.25 + 9.74 + + + 11.87 + 9.74 + + + 11.48 + 9.75 + + + 11.1 + 9.75 + + + 10.72 + 9.75 + + + 10.34 + 9.75 + + + 9.96 + 9.75 + + + 9.58 + 9.76 + + + 9.19 + 9.77 + + + 8.81 + 9.78 + + + 8.43 + 9.78 + + + 8.05 + 9.78 + + + 7.67 + 9.78 + + + 7.28 + 9.78 + + + 6.07 + 13.05 + + + 6.45 + 12.85 + + + 6.83 + 13.06 + + + 7.21 + 13.06 + + + 7.58 + 13.06 + + + 7.96 + 13.07 + + + 8.34 + 13.07 + + + 8.72 + 13.07 + + + 9.1 + 13.07 + + + 9.48 + 13.06 + + + 9.85 + 13.07 + + + 10.23 + 13.07 + + + 10.61 + 13.07 + + + 10.99 + 13.07 + + + 11.37 + 13.07 + + + 11.75 + 13.07 + + + 12.12 + 13.07 + + + 12.5 + 13.07 + + + 12.88 + 13.07 + + + 13.26 + 13.08 + + + 13.64 + 13.08 + + + 14.02 + 13.08 + + + 14.39 + 13.08 + + + 14.77 + 13.08 + + + 15.15 + 13.07 + + + 11.71 + 11.89 + + + 12.09 + 11.88 + + + 12.47 + 11.9 + + + 12.84 + 11.96 + + + 13.19 + 12.1 + + + 13.5 + 12.31 + + + 13.8 + 12.54 + + + 14.08 + 12.79 + + + 6.61 + 12.71 + + + 6.58 + 12.33 + + + 6.61 + 11.95 + + + 6.58 + 11.57 + + + 6.58 + 11.19 + + + 6.58 + 10.8 + + + 6.58 + 10.42 + + + 6.58 + 10.04 + + + 6.58 + 9.76 + + + 6.22 + 9.76 + + + 6.6 + 9.38 + + + 6.61 + 8.99 + + + 6.62 + 8.61 + + + 6.81 + 8.28 + + + 6.96 + 7.96 + + + 6.9 + 9.78 + + + 7.06 + 9.47 + + + 6.96 + 9.09 + + + 6.96 + 8.7 + + + 6.96 + 8.32 + + + 6.96 + 7.6 + + + 6.87 + 7.27 + + + 6.64 + 6.95 + + + 5.9 + 6.9 + + + 6.53 + 9.76 + + + + + + + False + + + False + + + False + + + False + + + False + + + + False + + + False + + + + False + + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + False + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + True + + + True + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + False + + + False + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + False + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + + False + + + False + + + + False + + + True + + + True + + + True + + + True + + + False + + + True + + + True + + + True + + + True + + + False + + + False + + + False + + + False + + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + + + \ No newline at end of file diff --git a/src/example/graphml/gercek2.graphml b/src/example/graphml/gercek2.graphml new file mode 100644 index 0000000..8228102 --- /dev/null +++ b/src/example/graphml/gercek2.graphml @@ -0,0 +1,3829 @@ + + + + + + + + + + + + + 9.34 + 0.54 + + + 10.04 + 0.54 + + + 10.99 + 0.54 + + + 11.71 + 0.54 + + + 12.41 + 0.54 + + + 9.34 + 1.3 + + + 10.03 + 1.3 + + + 10.73 + 1.3 + + + 11.41 + 1.3 + + + 13.11 + 1.3 + + + + + + 4.17 + 6.89 + + + 4.17 + 6.52 + + + 4.76 + 5.94 + + + 5.13 + 5.94 + + + 5.71 + 6.89 + + + 5.13 + 7.48 + + + 4.76 + 7.48 + + + 4.94 + 6.71 + + + 4.94 + 6.71 + + + 4.94 + 6.71 + + + 4.79 + 6.52 + + + 1.93 + 6.89 + + + 1.93 + 6.52 + + + 2.52 + 5.93 + + + 2.88 + 5.93 + + + 3.47 + 6.52 + + + 3.47 + 6.89 + + + 2.7 + 6.70 + + + 2.7 + 6.70 + + + 2.7 + 6.70 + + + 2.7 + 6.70 + + + 1.93 + 4.02 + + + 1.93 + 3.65 + + + 2.51 + 3.07 + + + 2.88 + 3.07 + + + 3.47 + 3.65 + + + 3.47 + 4.02 + + + 2.88 + 4.61 + + + 2.51 + 4.61 + + + 2.7 + 3.84 + + + 2.7 + 3.84 + + + 2.7 + 3.84 + + + 2.7 + 3.84 + + + 4.17 + 4.02 + + + 4.17 + 3.65 + + + 4.75 + 3.06 + + + 5.12 + 3.06 + + + 5.71 + 4.02 + + + 5.12 + 4.60 + + + 4.75 + 4.60 + + + 4.94 + 3.83 + + + 4.94 + 3.83 + + + 4.94 + 3.83 + + + 4.94 + 3.83 + + + 14.71 + 4.01 + + + 14.71 + 3.64 + + + 15.29 + 3.05 + + + 16.25 + 4.01 + + + 15.66 + 4.60 + + + 15.29 + 4.60 + + + 15.48 + 3.83 + + + 15.48 + 3.83 + + + 15.48 + 3.83 + + + 15.48 + 3.83 + + + 1.94 + 0.36 + + + 1.94 + 0.73 + + + 2.51 + 1.30 + + + 2.88 + 1.30 + + + 3.45 + 0.36 + + + 3.45 + 0.73 + + + 2.69 + 0.54 + + + 2.69 + 0.54 + + + 2.69 + 0.54 + + + 0.35 + 10.57 + + + 0.72 + 10.57 + + + 1.29 + 10.00 + + + 1.29 + 9.63 + + + 0.35 + 9.06 + + + 0.72 + 9.06 + + + 0.53 + 9.81 + + + 0.53 + 9.81 + + + 0.53 + 9.81 + + + 0.35 + 7.48 + + + 0.72 + 7.48 + + + 1.29 + 6.90 + + + 1.29 + 6.54 + + + 0.35 + 5.96 + + + 0.72 + 5.96 + + + 0.53 + 6.72 + + + 0.53 + 6.72 + + + 0.53 + 6.72 + + + 0.35 + 4.59 + + + 0.72 + 4.59 + + + 1.29 + 4.02 + + + 1.29 + 3.65 + + + 0.35 + 3.08 + + + 0.72 + 3.08 + + + 0.53 + 3.84 + + + 0.53 + 3.84 + + + 0.53 + 3.84 + + + 2.88 + 1.68 + + + 2.88 + 2.06 + + + 2.88 + 2.44 + + + 2.89 + 2.82 + + + 2.51 + 2.69 + + + 2.51 + 2.31 + + + 2.52 + 1.93 + + + 2.51 + 1.55 + + + 1.56 + 0.72 + + + 1.18 + 0.76 + + + 0.87 + 0.98 + + + 0.73 + 1.34 + + + 0.73 + 1.72 + + + 0.73 + 2.10 + + + 0.73 + 2.48 + + + 0.74 + 2.86 + + + 0.35 + 2.70 + + + 0.35 + 2.32 + + + 0.34 + 1.94 + + + 0.34 + 1.56 + + + 0.39 + 1.19 + + + 0.51 + 0.83 + + + 0.77 + 0.56 + + + 1.11 + 0.39 + + + 1.48 + 0.32 + + + 3.79 + 4.02 + + + 3.85 + 3.65 + + + 1.55 + 4.02 + + + 1.67 + 3.65 + + + 2.88 + 4.99 + + + 2.88 + 5.37 + + + 2.88 + 5.75 + + + 2.52 + 5.55 + + + 2.52 + 5.17 + + + 2.52 + 4.79 + + + 0.73 + 4.97 + + + 0.74 + 5.35 + + + 0.74 + 5.73 + + + 0.35 + 5.59 + + + 0.34 + 5.21 + + + 0.35 + 4.83 + + + 1.67 + 6.53 + + + 1.55 + 6.88 + + + 3.85 + 6.52 + + + 3.79 + 6.88 + + + 5.13 + 7.86 + + + 5.11 + 8.24 + + + 5.04 + 8.61 + + + 4.9 + 8.97 + + + 4.71 + 9.30 + + + 4.46 + 9.59 + + + 4.17 + 9.84 + + + 3.84 + 10.03 + + + 3.48 + 10.14 + + + 3.1 + 10.19 + + + 2.72 + 10.19 + + + 2.35 + 10.11 + + + 1.97 + 10.03 + + + 1.59 + 10.01 + + + 1.67 + 9.61 + + + 2.05 + 9.62 + + + 2.4 + 9.75 + + + 2.77 + 9.84 + + + 3.15 + 9.83 + + + 3.52 + 9.74 + + + 3.87 + 9.59 + + + 4.17 + 9.37 + + + 4.42 + 9.08 + + + 4.64 + 8.78 + + + 4.73 + 8.41 + + + 4.76 + 8.04 + + + 4.76 + 7.66 + + + 0.72 + 7.86 + + + 0.73 + 8.24 + + + 0.73 + 8.62 + + + 0.34 + 8.68 + + + 0.34 + 8.30 + + + 0.34 + 7.92 + + + 3.83 + 0.35 + + + 4.21 + 0.35 + + + 4.59 + 0.35 + + + 4.97 + 0.35 + + + 5.34 + 0.35 + + + 5.72 + 0.35 + + + 6.1 + 0.36 + + + 6.44 + 0.54 + + + 6.65 + 0.86 + + + 6.66 + 1.24 + + + 6.63 + 1.62 + + + 6.62 + 2.00 + + + 6.63 + 2.38 + + + 6.63 + 2.76 + + + 6.62 + 3.14 + + + 6.61 + 3.52 + + + 6.71 + 4.05 + + + 6.1 + 4.02 + + + 4.76 + 2.68 + + + 4.76 + 2.31 + + + 4.76 + 1.93 + + + 4.75 + 1.55 + + + 4.75 + 1.17 + + + 4.8 + 0.67 + + + 4.35 + 0.77 + + + 3.97 + 0.75 + + + 4.8 + 0.44 + + + 5.07 + 0.93 + + + 5.14 + 1.31 + + + 5.16 + 1.69 + + + 5.15 + 2.07 + + + 5.12 + 2.45 + + + 5.14 + 2.83 + + + 5.13 + 4.98 + + + 5.13 + 5.37 + + + 5.14 + 5.75 + + + 4.75 + 5.56 + + + 4.75 + 5.18 + + + 4.73 + 4.80 + + + 7.03 + 0.89 + + + 7.41 + 0.90 + + + 7.79 + 0.91 + + + 8.17 + 0.90 + + + 8.54 + 0.90 + + + 8.92 + 0.90 + + + 9.3 + 0.90 + + + 9.68 + 0.91 + + + 10.06 + 0.91 + + + 10.44 + 0.92 + + + 10.82 + 0.92 + + + 11.2 + 0.93 + + + 11.58 + 0.93 + + + 11.95 + 0.93 + + + 12.33 + 0.93 + + + 12.71 + 0.93 + + + 13.09 + 0.92 + + + 13.47 + 0.92 + + + 13.85 + 0.92 + + + 14.23 + 0.92 + + + 14.61 + 0.93 + + + 14.99 + 0.94 + + + 15.36 + 0.94 + + + 15.74 + 0.94 + + + 16.12 + 0.94 + + + 16.5 + 0.92 + + + 16.84 + 1.10 + + + 17.08 + 1.39 + + + 17.08 + 1.77 + + + 17.08 + 2.15 + + + 17.08 + 2.53 + + + 17.08 + 2.91 + + + 17.08 + 3.30 + + + 17.02 + 3.67 + + + 16.71 + 3.90 + + + 15.3 + 2.68 + + + 15.3 + 2.30 + + + 15.28 + 1.92 + + + 15.29 + 1.54 + + + 15.32 + 1.16 + + + 11.77 + 2.05 + + + 12.15 + 2.05 + + + 12.53 + 2.03 + + + 12.91 + 1.99 + + + 13.24 + 1.80 + + + 13.55 + 1.58 + + + 13.85 + 1.35 + + + 14.14 + 1.11 + + + 14.32 + 4.02 + + + 13.94 + 4.01 + + + 13.56 + 4.01 + + + 13.18 + 4.01 + + + 12.8 + 4.01 + + + 12.42 + 4.01 + + + 12.04 + 4.01 + + + 11.66 + 4.00 + + + 11.28 + 4.00 + + + 10.9 + 4.00 + + + 10.51 + 4.01 + + + 10.13 + 4.01 + + + 9.75 + 4.01 + + + 9.37 + 4.01 + + + 8.99 + 4.01 + + + 8.61 + 4.01 + + + 8.23 + 4.01 + + + 7.85 + 4.01 + + + 7.47 + 4.01 + + + 7.09 + 4.01 + + + 6.71 + 4.05 + + + 6.64 + 4.33 + + + 6.64 + 4.71 + + + 6.64 + 5.09 + + + 6.78 + 5.44 + + + 7.0 + 5.75 + + + 7.01 + 6.13 + + + 6.95 + 6.51 + + + 6.7 + 6.80 + + + 6.33 + 6.89 + + + 5.95 + 6.91 + + + 7.02 + 4.38 + + + 7.0 + 4.76 + + + 6.99 + 5.15 + + + 15.66 + 4.98 + + + 15.66 + 5.36 + + + 15.66 + 5.74 + + + 15.67 + 6.12 + + + 15.67 + 6.50 + + + 15.68 + 6.88 + + + 15.88 + 7.21 + + + 16.1 + 7.52 + + + 16.21 + 7.88 + + + 16.25 + 8.26 + + + 16.24 + 8.64 + + + 16.25 + 9.02 + + + 16.25 + 9.40 + + + 15.87 + 9.45 + + + 15.87 + 9.08 + + + 15.87 + 8.70 + + + 15.87 + 8.32 + + + 15.86 + 7.94 + + + 15.74 + 7.58 + + + 15.54 + 7.26 + + + 15.36 + 6.93 + + + 15.29 + 6.55 + + + 15.3 + 6.17 + + + 15.31 + 5.80 + + + 15.3 + 5.42 + + + 15.28 + 5.04 + + + 16.25 + 9.90 + + + 16.52 + 10.17 + + + 16.81 + 10.42 + + + 16.72 + 10.79 + + + 16.46 + 11.07 + + + 16.11 + 11.22 + + + 15.74 + 11.13 + + + 15.45 + 10.88 + + + 15.4 + 10.51 + + + 15.53 + 10.15 + + + 15.8 + 9.89 + + + 16.32 + 11.43 + + + 16.28 + 11.81 + + + 16.26 + 12.19 + + + 16.28 + 12.57 + + + 16.48 + 12.89 + + + 16.83 + 13.02 + + + 17.21 + 13.03 + + + 17.59 + 13.02 + + + 17.97 + 13.02 + + + 18.35 + 13.02 + + + 18.73 + 13.02 + + + 19.11 + 13.02 + + + 19.49 + 13.00 + + + 19.78 + 12.76 + + + 19.94 + 12.42 + + + 19.94 + 12.04 + + + 19.94 + 11.66 + + + 19.94 + 11.28 + + + 19.78 + 10.93 + + + 19.43 + 10.77 + + + 19.06 + 10.74 + + + 18.67 + 10.74 + + + 18.29 + 10.74 + + + 17.91 + 10.73 + + + 17.53 + 10.75 + + + 17.15 + 10.77 + + + 17.18 + 10.39 + + + 17.56 + 10.38 + + + 17.94 + 10.38 + + + 18.32 + 10.39 + + + 18.7 + 10.40 + + + 19.08 + 10.39 + + + 19.46 + 10.40 + + + 19.82 + 10.50 + + + 20.1 + 10.75 + + + 20.28 + 11.09 + + + 20.36 + 11.46 + + + 20.34 + 11.84 + + + 20.31 + 12.22 + + + 20.24 + 12.60 + + + 20.12 + 12.96 + + + 19.88 + 13.25 + + + 19.52 + 13.39 + + + 19.14 + 13.44 + + + 18.76 + 13.45 + + + 18.38 + 13.45 + + + 18.0 + 13.44 + + + 17.62 + 13.45 + + + 17.24 + 13.45 + + + 16.86 + 13.42 + + + 16.49 + 13.32 + + + 16.2 + 13.08 + + + 16.01 + 12.75 + + + 15.9 + 12.39 + + + 15.88 + 12.01 + + + 15.88 + 11.63 + + + 15.08 + 10.79 + + + 14.7 + 10.77 + + + 14.32 + 10.77 + + + 13.94 + 10.76 + + + 13.94 + 10.35 + + + 14.32 + 10.36 + + + 14.7 + 10.35 + + + 15.08 + 10.35 + + + 0.74 + 10.95 + + + 0.79 + 11.33 + + + 0.98 + 11.66 + + + 1.34 + 11.77 + + + 1.72 + 11.78 + + + 2.1 + 11.78 + + + 2.48 + 11.78 + + + 2.86 + 11.78 + + + 3.24 + 11.78 + + + 3.62 + 11.78 + + + 4.0 + 11.78 + + + 4.37 + 11.78 + + + 4.75 + 11.79 + + + 5.13 + 11.79 + + + 5.51 + 11.78 + + + 5.89 + 11.78 + + + 6.27 + 11.78 + + + 6.65 + 11.77 + + + 6.7 + 12.16 + + + 6.32 + 12.16 + + + 5.94 + 12.16 + + + 5.56 + 12.15 + + + 5.18 + 12.15 + + + 4.8 + 12.15 + + + 4.42 + 12.16 + + + 4.04 + 12.16 + + + 3.66 + 12.16 + + + 3.27 + 12.15 + + + 2.89 + 12.15 + + + 2.51 + 12.15 + + + 2.13 + 12.15 + + + 1.75 + 12.16 + + + 1.37 + 12.15 + + + 1.0 + 12.07 + + + 0.68 + 11.86 + + + 0.47 + 11.54 + + + 0.34 + 11.19 + + + 0.33 + 10.81 + + + 13.72 + 11.07 + + + 13.35 + 11.18 + + + 12.97 + 11.18 + + + 12.59 + 11.18 + + + 12.21 + 11.19 + + + 11.83 + 11.24 + + + 11.47 + 11.36 + + + 11.11 + 11.49 + + + 10.78 + 11.68 + + + 10.45 + 11.87 + + + 10.11 + 12.05 + + + 9.77 + 12.22 + + + 9.41 + 12.36 + + + 9.05 + 12.47 + + + 8.68 + 12.55 + + + 8.3 + 12.60 + + + 7.92 + 12.59 + + + 7.54 + 12.58 + + + 7.16 + 12.57 + + + 6.9 + 11.50 + + + 7.24 + 11.32 + + + 7.62 + 11.34 + + + 8.0 + 11.34 + + + 8.38 + 11.33 + + + 8.75 + 11.27 + + + 9.12 + 11.18 + + + 9.46 + 11.03 + + + 9.81 + 10.88 + + + 10.15 + 10.71 + + + 10.48 + 10.52 + + + 10.81 + 10.33 + + + 11.15 + 10.18 + + + 11.52 + 10.08 + + + 11.89 + 10.00 + + + 12.27 + 9.98 + + + 12.64 + 9.96 + + + 13.02 + 9.94 + + + 13.4 + 9.94 + + + 13.78 + 9.98 + + + 13.56 + 10.75 + + + 13.18 + 10.75 + + + 12.8 + 10.74 + + + 12.42 + 10.77 + + + 12.04 + 10.81 + + + 11.66 + 10.87 + + + 11.3 + 10.99 + + + 10.95 + 11.14 + + + 10.61 + 11.32 + + + 10.28 + 11.51 + + + 9.94 + 11.68 + + + 9.6 + 11.86 + + + 9.25 + 12.01 + + + 8.88 + 12.11 + + + 8.5 + 12.15 + + + 8.13 + 12.20 + + + 7.74 + 12.20 + + + 7.36 + 12.20 + + + 6.98 + 12.20 + + + 7.03 + 11.76 + + + 7.41 + 11.76 + + + 7.78 + 11.77 + + + 8.16 + 11.76 + + + 8.54 + 11.75 + + + 8.91 + 11.66 + + + 9.27 + 11.54 + + + 9.62 + 11.41 + + + 9.96 + 11.23 + + + 10.3 + 11.06 + + + 10.64 + 10.90 + + + 10.97 + 10.72 + + + 11.33 + 10.59 + + + 11.69 + 10.48 + + + 12.06 + 10.42 + + + 12.44 + 10.37 + + + 12.82 + 10.37 + + + 13.2 + 10.37 + + + 13.58 + 10.36 + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + True + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + False + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + False + + + False + + + False + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + + + + + + + False + + + False + + + True + + + True + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + True + + + True + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + + + + + + + + + + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + + True + + + \ No newline at end of file diff --git a/src/example/include/mekatronom/MpcNode.hpp b/src/example/include/mekatronom/MpcNode.hpp new file mode 100644 index 0000000..4b471dc --- /dev/null +++ b/src/example/include/mekatronom/MpcNode.hpp @@ -0,0 +1,228 @@ +#pragma once + +#include "ros/ros.h" +#include "std_msgs/Bool.h" +#include "std_msgs/String.h" +#include "sensor_msgs/Imu.h" +#include "utils/IMU.h" // Assuming 'utils' is the package name and 'IMU' is the message type +#include "utils/localisation.h" // Assuming 'localisation' is the message type +#include +#include +#include "casadi/casadi.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "tf2_geometry_msgs/tf2_geometry_msgs.h" +#include +#include +#include +#include "obstacle_detector/Obstacles.h" + +using namespace casadi; +using json = nlohmann::json; + +namespace fs = std::filesystem; + +class MpcNode { +public: + // Constructor and Destructor + MpcNode(ros::NodeHandle& nh, ros::NodeHandle& nh_local); + ~MpcNode(); + + ros::Publisher carControl_pub_; + + double new_point_counter_ = 600; + + typedef struct { + double distance; + std::vector atalist; + bool pass_through; + double x; + double y; + } NodeInfo; + NodeInfo node_info_; + + typedef struct { + double x; + double y; + double z; + double roll; + double pitch; + double yaw; + } Pose; + Pose localisation_data_; + + typedef struct { + int obstacles_checking; + int watchdogTimer; + int went_node; + } Timers; + Timers last_update_time_; + + typedef struct { + double Q_x; + double Q_y; + double Q_theta; + double R1; + double R2; + double step_horizon; + int N; + double rob_diam; + double wheel_radius; + double L_value; + double Ly; + double v_max; + double v_min; + double omega_max; + double omega_min; + int iteration; + double goal_checker{0.2}; + std::vector parking_nodes_id{"900","901","902","903","904","910","911","912","913","914"}; + std::vector parking_spot_is_full; + std::string source_node{"263"}; + std::string target_node{"900"}; + std::vector dont_check_obstacles_this_nodes{"228","229","230","231","232","233","234","235","236","237","238","239","240"}; + std::vector excluded_nodes = {"273"}; + std::vector obstacles_array = {" "}; + std::vector parkings_are_available{" " }; + } Settings; + Settings initial_settings_; + + typedef struct { + std::vector> SourceTargetNodesOriginal; + std::vector> SourceTargetNodes; + std::vector> SourceTargetNodesCopy; + std::vector> pathGoalsYawDegree; + std::vector> pathGoalsYawDegreeOriginal; + std::vector> pathGoalsYawDegreeCopy; + std::map> obstacle_node_positions; + bool pass_through{false}; + std::map node_dict; + } djikstra_outputs; + djikstra_outputs djikstra_outputs_; + + typedef struct { + DM u0; + DM X0; + DM state_init; + DM state_target; + DM next_state; + int n_states; + int n_controls; + std::map args; + Function solver; + DM cat_states; + DM cat_controls; + Function f; + double steerAngle; + double steerLateral; + DM u; + int last_path_index; + } Control; + Control mpc_setting_outputs_; + + std::vector> nodes_data_; + std::vector> edges_data_; + std::vector> edges_data_true_ilkverisyon_; + std::string car_behaviour_state_ = "keep_lane"; + + std::vector center_x_, center_y_; + std::vector> pathOriginal_; + std::vector> path_; + std::map> new_node_data_; + std::map> obs_dict_; + std::vector expath_; + std::vector shortest_path_; + std::tuple goal_id_; + + int checking_counter_{0}; + bool pathGoalsYawDegreecalled_{false}; + bool distance_flag_{false}; + std::string closest_node_id_original_; + std::string closest_node_id_; + + std::string graphml_file_path_; + std::string scenerio_name_; + std::string graphml_filename_ = "fixed2.graphml"; + + + // Public Functions for mpc_running.h + void shiftTimestep(MpcNode& node); + void carControlPublisher(MpcNode& node); + // int calculateClosestNodeId(const std::vector>& pathGoalsYawDegree, + // double position_x, double position_y); + + + std::string find_file(const std::string& filename) { + fs::path current_path = fs::current_path(); + for (const auto& entry : fs::recursive_directory_iterator(current_path)) { + if (entry.path().filename() == filename) { + return entry.path().string(); + } + } + throw std::runtime_error("File not found: " + filename); + } + +private: + + // Private Functions + // void imageCb(const sensor_msgs::Image::ConstPtr& msg); + void controlCb(const ros::TimerEvent&); + void imuCb(const sensor_msgs::Imu::ConstPtr& msg); + void localisationCb(const geometry_msgs::PoseWithCovarianceStamped::ConstPtr& msg); + void obstacleDetectionCb(const obstacle_detector::Obstacles::ConstPtr& msg); + void process_and_publish_data(int temp_source, int temp_target); + + void function_one(); + void get_parameters(); + + int control_rate_{30}; + bool mpc_started_{ false }; + bool imu_data_received_ = false; + bool localisation_data_received_ = false; + bool graphml_file_path_is_set_ = false; + // Timers + ros::Timer control_timer_; + + // Subscribers + ros::Subscriber imu_sub_; + ros::Subscriber localisation_sub_; + ros::Subscriber image_sub_; + ros::Subscriber obstacle_detection_sub_; + // Publishers + ros::Publisher cmd_pub_; + // Node Handle + ros::NodeHandle nh_; + ros::NodeHandle nh_local_; + + sensor_msgs::Image image_data_; + +}; + +template +void print_tuple_impl(std::ostream& os, const T& t, std::index_sequence) { + ((os << (Is == 0 ? "" : ", ") << std::get(t)), ...); +} + +template +std::ostream& operator<<(std::ostream& os, const std::tuple& t) { + os << "("; + print_tuple_impl(os, t, std::index_sequence_for{}); + os << ")"; + return os; +} + +#include "mekatronom/utilities/djikstra.h" +#include "mekatronom/utilities/traffic_sign_manager.h" +#include "mekatronom/utilities/mpc_running.h" +#include "mekatronom/utilities/mpc_start_setting.h" + diff --git a/src/example/include/mekatronom/utilities/djikstra.h b/src/example/include/mekatronom/utilities/djikstra.h new file mode 100644 index 0000000..1592393 --- /dev/null +++ b/src/example/include/mekatronom/utilities/djikstra.h @@ -0,0 +1,624 @@ +/* +* MIT License +* +* Copyright (c) 2024 Mehmet Baha Dursun +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +/* + * Author: Mehmet Baha Dursun + */ + +#pragma once + +#include "mekatronom/MpcNode.hpp" + +// enable this for verbose debug information +// #define DEBUG + +class processAndPublishPath { +public: + processAndPublishPath(std::string& graphml_file_path_,std::string source_node_, std::string target_node_, MpcNode& mpc_node) + { + + clock_t stx = clock(); + pugi::xml_document doc; + pugi::xml_parse_result result = doc.load_file(graphml_file_path_.c_str()); + ROS_INFO("RESULT: %s", result.description()); + + if (!result) { + ROS_ERROR("Error loading XML file: %s", result.description()); + return; + } + pugi::xml_node root = doc.child("graphml"); + if (!root) { + ROS_ERROR("Error: no root node found in the XML file."); + return; + } + pugi::xml_node graph = root.child("graph"); + if (!graph) { + ROS_ERROR("Error: no graph node found in the XML file."); + return; + } + + mpc_node.nodes_data_ = extract_nodes_data(graph); + + for (const auto& node : mpc_node.nodes_data_ ) { + const auto& node_id = std::get<0>(node); + if (std::find(mpc_node.initial_settings_.parking_nodes_id.begin(), + mpc_node.initial_settings_.parking_nodes_id.end(), node_id) != mpc_node.initial_settings_.parking_nodes_id.end()) { + double x = std::get<1>(node); + double y = std::get<2>(node); + mpc_node.djikstra_outputs_.obstacle_node_positions[node_id] = std::make_pair(x, y); + } + } + + mpc_node.edges_data_ = extract_edges_data(graph); + + mpc_node.djikstra_outputs_.pass_through = false; + for (const auto& edge : mpc_node.edges_data_) { + for (const auto& obs : mpc_node.initial_settings_.excluded_nodes) { + if (std::get<2>(edge)) { + mpc_node.djikstra_outputs_.pass_through = true; + break; + } + } + if (mpc_node.djikstra_outputs_.pass_through) break; + } + + if (!mpc_node.djikstra_outputs_.pass_through) { + mpc_node.djikstra_outputs_.pass_through = false; + } + +#ifdef DEBUG + std::cout << "\n\n\nNext section is mpc_node.nodes_data_:\n "; + for (const auto& node : mpc_node.nodes_data_) { + std::cout << std::get<0>(node) << ": (" << std::get<1>(node) << ", " << std::get<2>(node) << ") "; + } + std::cout << std::endl; + + std::cout << "\n\n\next section is nmpc_node.edges_data_: \n"; + for (const auto& edge : mpc_node.edges_data_) { + std::cout << std::get<0>(edge) << " -> " << std::get<1>(edge) << " (" << (std::get<2>(edge) ? "true" : "false") << ") "; + } + std::cout << std::endl; +#endif + std::map noded; + std::map>> edged; + std::tie(noded, edged) = extract_graph(mpc_node.nodes_data_, mpc_node.edges_data_); + +#ifdef DEBUG + std::cout << "\n\n\nnoded size: " << noded.size() << " edged size: " << edged.size() << std::endl; + + std::cout << "\n\n\nOutputs of noded:" << std::endl; + for (const auto& [key, value] : noded) { + std::cout << key << ": (" + << "distance: " << value.distance << ", " + << "pass_through: " << std::boolalpha << value.pass_through << ", " + << "x: " << value.x << ", " + << "y: " << value.y << ", " + << "atalist: ["; + + for (size_t i = 0; i < value.atalist.size(); ++i) { + if (i > 0) std::cout << ", "; + std::cout << value.atalist[i]; + } + std::cout << "])" << std::endl; + } + + std::cout << "\n\n\nOutputs of edged:" << std::endl; + for (const auto& [key, value] : edged) { + std::cout << key << ": "; + for (const auto& v : value) { + std::cout << "(" << v.first << ", " << v.second << ") "; + } + std::cout << " "; + } + std::cout << std::endl; +#endif + + mpc_node.shortest_path_ = dijkstra(source_node_, target_node_, noded, edged, mpc_node.initial_settings_.excluded_nodes, mpc_node); + + mpc_node.pathOriginal_ = stformat(mpc_node.shortest_path_); + mpc_node.djikstra_outputs_.node_dict = noded; + + + auto [new_node_data, stlist] = beizer(mpc_node.shortest_path_, noded, mpc_node); + mpc_node.new_node_data_ = new_node_data; + + ROS_INFO("\n\nCalculated path is :", mpc_node.shortest_path_); + +#ifdef DEBUG + std::cout << "\n\n\nmpc_node.shortest_path_ "<< mpc_node.shortest_path_ << std::endl; + + std::cout << "\n\n\nnoded size: " << noded.size() << " edged size: " << edged.size() << std::endl; + + std::cout << "\n\n\nOutputs of noded:" << std::endl; + for (const auto& [key, value] : noded) { + std::cout << key << ": (" + << "distance: " << value.distance << ", " + << "pass_through: " << std::boolalpha << value.pass_through << ", " + << "x: " << value.x << ", " + << "y: " << value.y << ", " + << "atalist: ["; + + for (size_t i = 0; i < value.atalist.size(); ++i) { + if (i > 0) std::cout << ", "; + std::cout << value.atalist[i]; + } + std::cout << "])" << std::endl; + } + + std::cout << "\n\n\nOutputs of edged:" << std::endl; + for (const auto& [key, value] : edged) { + std::cout << key << ": "; + for (const auto& v : value) { + std::cout << "(" << v.first << ", " << v.second << ") "; + } + std::cout << " "; + } + std::cout << std::endl; + + + std::cout << "\n\n\nNew node data:" << mpc_node.new_node_data_ << std::endl; + std::cout << "\n\n\nShortest path:" << mpc_node.shortest_path_ << std::endl; + std::cout << "\n\n\nPath original:" << mpc_node.pathOriginal_ << std::endl; +#endif + + for (const auto& edge : stlist) { + if (std::get<2>(edge)) { + mpc_node.djikstra_outputs_.SourceTargetNodes.emplace_back(std::get<0>(edge), std::get<1>(edge)); + } + } + + for (const auto& edge : mpc_node.pathOriginal_) { + if (std::get<2>(edge)) { + mpc_node.djikstra_outputs_.SourceTargetNodesOriginal.emplace_back(std::get<0>(edge), std::get<1>(edge)); + } + } + + std::vector> path; + std::vector> path_original; + + for (const auto& [source_id, target_id] : mpc_node.djikstra_outputs_.SourceTargetNodesOriginal) { + if (mpc_node.new_node_data_.find(source_id) != mpc_node.new_node_data_.end()) { + const auto& source_coords = mpc_node.new_node_data_.at(source_id); + path_original.emplace_back(std::stoi(source_id), source_coords.first, source_coords.second); + } + if (mpc_node.new_node_data_.find(target_id) != mpc_node.new_node_data_.end()) { + const auto& coords = mpc_node.new_node_data_.at(target_id); + path_original.emplace_back(std::stoi(target_id), coords.first, coords.second); + } + } + + for (const auto& [source_id, target_id] : mpc_node.djikstra_outputs_.SourceTargetNodes) { + try { + if (mpc_node.new_node_data_.find(target_id) != mpc_node.new_node_data_.end()) { + const auto& coords = mpc_node.new_node_data_.at(target_id); + path.emplace_back(std::stoi(target_id), coords.first, coords.second); + } else { + ROS_ERROR("target_id %s not found in obs_dict_", target_id.c_str()); + } + } catch (const std::invalid_argument& e) { + ROS_ERROR("Invalid target_id: %s", target_id.c_str()); + } catch (const std::out_of_range& e) { + ROS_ERROR("target_id out of range: %s", target_id.c_str()); + } + } + + std::vector angles, anglesOriginal; + + for (size_t i = 0; i < path_original.size() - 1; ++i) { + try { + double dx = std::get<1>(path_original[i + 1]) - std::get<1>(path_original[i]); + double dy = std::get<2>(path_original[i + 1]) - std::get<2>(path_original[i]); + + double angle = std::atan2(dy, dx); + anglesOriginal.push_back(angle); + } catch (const std::invalid_argument& e) { + ROS_ERROR("Invalid argument: %s at index %d", e.what(), i); + continue; + } catch (const std::out_of_range& e) { + ROS_ERROR("Out of range: %s at index %d", e.what(), i); + continue; + } + } + + for (size_t i = 0; i < path.size() - 1; ++i) { + double dx = std::get<1>(path[i+1]) - std::get<1>(path[i]); + double dy = std::get<2>(path[i+1]) - std::get<2>(path[i]); + + double angle = std::atan2(dy, dx); + angles.push_back(angle); + } + + if (!angles.empty()) { + angles.push_back(angles.back()); + } + + if (!anglesOriginal.empty()) { + anglesOriginal.push_back(anglesOriginal.back()); + } + + for (size_t i = 0; i < path.size(); ++i) { + mpc_node.djikstra_outputs_.pathGoalsYawDegree.emplace_back(std::get<0>(path[i]), std::get<1>(path[i]), std::get<2>(path[i]), angles[i]); + } + + for (size_t i = 0; i < path_original.size(); ++i) { + mpc_node.djikstra_outputs_.pathGoalsYawDegreeOriginal.emplace_back(std::get<0>(path_original[i]), std::get<1>(path_original[i]), std::get<2>(path_original[i]), anglesOriginal[i]); + } + +#ifdef DEBUG + std::cout << "\n\n\nOutputs of Path:" << std::endl; + for (const auto& [id, x, y] : path) { + std::cout << "Node ID: " << id << ", X: " << x << ", Y: " << y << std::endl; + } + + std::cout << "\n\n\nOutputs of path_original:" << std::endl; + for(const auto& [id, x, y] : path_original) + { + std::cout << "ID: " << id << ", X: " << x << ", Y: " << y << std::endl; + } + + std::cout << "\n\n\nOutputs of angles:" << std::endl; + for (const auto& angle : angles) { + std::cout << "Angle: " << angle << std::endl; + } + + std::cout << "\n\n\nOutputs of pathGoalsYawDegree_:" << std::endl; + for (const auto& [id, x, y, angle] : mpc_node.djikstra_outputs_.pathGoalsYawDegree) { + std::cout << "Node ID1: " << id << ", X1: " << x << ", Y1: " << y << ", Angle1: " << angle << std::endl; + } + + std::cout << "\n\n\nOutputs of pathGoalsYawDegreeOriginal_:" << std::endl; + for (const auto& [id, x, y, angle] : mpc_node.djikstra_outputs_.pathGoalsYawDegreeOriginal) { + std::cout << "Node ID2: " << id << ", X2: " << x << ", Y2: " << y << ", Angle2: " << angle << std::endl; + } + + clock_t etx = clock(); + double elapsed_timex = double(etx - stx) / CLOCKS_PER_SEC; + std::cout << "\n\n\nPasted time for DJikstra calculation:" << elapsed_timex << "seconds" << std::endl; +#endif + if (!mpc_node.pathGoalsYawDegreecalled_) { + mpc_node.djikstra_outputs_.pathGoalsYawDegreeCopy = mpc_node.djikstra_outputs_.pathGoalsYawDegreeOriginal; + mpc_node.djikstra_outputs_.SourceTargetNodesCopy = mpc_node.djikstra_outputs_.SourceTargetNodesOriginal; + mpc_node.pathGoalsYawDegreecalled_ = true; + } + + } + + + std::vector dijkstra(const std::string& source, const std::string& target, + std::map& nodedict, + std::map>>& edgedict, + const std::vector& excluded_nodes, + MpcNode& mpc_node) { + + std::cout << "____________________" << source << std::endl; + std::cout << "____________________" << target << std::endl; + + std::vector nowaypoints_source; + std::vector nowaypoints_target; + + for (auto& ed : edgedict) { + for (auto it = ed.second.begin(); it != ed.second.end(); ) { + if (std::find(excluded_nodes.begin(), excluded_nodes.end(), it->first) != excluded_nodes.end()) { + nowaypoints_source.push_back(ed.first); + nowaypoints_target.push_back(it->first); + it = ed.second.erase(it); + } else { + ++it; + } + } + } + + std::unordered_map unvisited; + std::unordered_map visited; + std::unordered_map> paths; + + for (const auto& edge : edgedict) { + unvisited[edge.first] = std::numeric_limits::infinity(); + } + unvisited[source] = 0; + paths[source] = {source}; + + while (!unvisited.empty()) { + // Find the node with the minimum distance + auto minNode = *std::min_element(unvisited.begin(), unvisited.end(), + [](const auto& lhs, const auto& rhs) { return lhs.second < rhs.second; }); + + visited[minNode.first] = minNode.second; + + // Update distances to neighbors + for (const auto& neighbor : edgedict[minNode.first]) { + if (visited.find(neighbor.first) != visited.end()) continue; + + double newDistance = visited[minNode.first] + neighbor.second; + + if (newDistance < unvisited[neighbor.first]) { + unvisited[neighbor.first] = newDistance; + paths[neighbor.first] = paths[minNode.first]; + paths[neighbor.first].push_back(neighbor.first); + + nodedict[neighbor.first].distance = newDistance; + nodedict[neighbor.first].atalist = paths[neighbor.first]; + } + } + + unvisited.erase(minNode.first); + } + + std::vector path = nodedict[target].atalist; + + if (std::find(path.begin(), path.end(), source) != path.end() && + std::find(path.begin(), path.end(), target) != path.end()) { + + bool traffic_light_state = false; + bool pedesterian = false; + bool is_past_path_is_shortest = false; + + + if (traffic_light_state || pedesterian || is_past_path_is_shortest) { + + std::string bagendxx; + for (const auto& tempstopxx : excluded_nodes) { + if (std::find(mpc_node.expath_.begin(), mpc_node.expath_.end(), tempstopxx) != mpc_node.expath_.end()) { + bagendxx = tempstopxx; + break; + } + } + + auto indexnoEXxx = std::find(mpc_node.expath_.begin(), mpc_node.expath_.end(), bagendxx) - mpc_node.expath_.begin(); + + std::string befbagendxx; + if (mpc_node.expath_.size() > 2) { + befbagendxx = mpc_node.expath_[indexnoEXxx - 1]; + } else { + befbagendxx = mpc_node.expath_[indexnoEXxx]; + } + + nodedict[befbagendxx].atalist.push_back(befbagendxx); + return nodedict[befbagendxx].atalist; + } else { + auto yolvar = true; + mpc_node.expath_ = nodedict[target].atalist; + return nodedict[target].atalist; + } + } else { + std::string bagend; + for (const auto& tempstopx : excluded_nodes) { + if (std::find(mpc_node.expath_.begin(), mpc_node.expath_.end(), tempstopx) != mpc_node.expath_.end()) { + bagend = tempstopx; + break; + } + } + auto indexnoEX = std::find(mpc_node.expath_.begin(), mpc_node.expath_.end(), bagend) - mpc_node.expath_.begin(); + std::string befbagend; + if (mpc_node.expath_.size() > 2) { + befbagend = mpc_node.expath_[indexnoEX - 2]; + } else { + befbagend = mpc_node.expath_[indexnoEX]; + } + + nodedict[befbagend].atalist.push_back(befbagend); + return nodedict[befbagend].atalist; + } + + return nodedict[target].atalist; + } + + std::vector convertPathToString(const std::vector& int_path) { + std::vector string_path; + for (int node : int_path) { + string_path.push_back(std::to_string(node)); + } + return string_path; + } + + std::pair>, std::vector>> + beizer(const std::vector& path, std::map& node_d, MpcNode& mpc_node) { + + std::map> new_node_data; + for (const auto& [idkey, node] : node_d) { + new_node_data[idkey] = std::make_pair(node.x, node.y); + } + + int new_point_counter = 600; + std::vector new_path = path; + size_t path_length = path.size(); + int ppbuk = 0; + + for (size_t f = 0; f < path_length - 2; ++f) { + double angel_rad1, angel_rad2; + + if (node_d[path[f]].x == node_d[path[f + 1]].x) { + angel_rad1 = 1.57; + } else { + angel_rad1 = std::atan((node_d[path[f]].y - node_d[path[f + 1]].y) / (node_d[path[f]].x - node_d[path[f + 1]].x)); + } + double angel_deg1 = angel_rad1 * 57.3; + + if (node_d[path[f + 1]].x == node_d[path[f + 2]].x) { + angel_rad2 = 1.57; + } else { + angel_rad2 = std::atan((node_d[path[f + 1]].y - node_d[path[f + 2]].y) / (node_d[path[f + 1]].x - node_d[path[f + 2]].x)); + } + double angel_deg2 = angel_rad2 * 57.3; + + double b_andgel = std::abs(angel_deg1 - angel_deg2); + int numout = 1; + + if (b_andgel > 55 && b_andgel < 110) { + ppbuk = f; + + std::vector temp_new_nodelist; + + if (true) { + int numPts = 2; + numout = 2; + + std::vector> controlPts = { + {node_d[path[f]].x, node_d[path[f]].y}, + {node_d[path[f + 1]].x, node_d[path[f + 1]].y}, + {node_d[path[f + 2]].x, node_d[path[f + 2]].y} + }; + + std::vector t(numPts + 1); + for (int i = 0; i <= numPts; ++i) { + t[i] = i * 1.0 / numPts; + } + + std::vector B_x(numPts + 1), B_y(numPts + 1); + for (int i = 0; i <= numPts; ++i) { + B_x[i] = (1 - t[i]) * ((1 - t[i]) * controlPts[0].first + t[i] * controlPts[1].first) + t[i] * ((1 - t[i]) * controlPts[1].first + t[i] * controlPts[2].first); + B_y[i] = (1 - t[i]) * ((1 - t[i]) * controlPts[0].second + t[i] * controlPts[1].second) + t[i] * ((1 - t[i]) * controlPts[1].second + t[i] * controlPts[2].second); + } + + for (int new_p = 1; new_p < numPts; ++new_p) { + new_point_counter++; + std::string new_point_str = std::to_string(new_point_counter); + + new_node_data[new_point_str] = std::make_pair(B_x[new_p], B_y[new_p]); + temp_new_nodelist.push_back(new_point_str); + } + new_path.erase(new_path.begin() + f + 1); + } + + size_t temp_index = f + 1; + for (const auto& insrt : temp_new_nodelist) { + new_path.insert(new_path.begin() + temp_index, insrt); + temp_index++; + } + } + } + + std::vector> source_target; + path_length = new_path.size(); + for (size_t n_edge = 0; n_edge < path_length - 1; ++n_edge) { + source_target.emplace_back(new_path[n_edge], new_path[n_edge + 1], true); + } + + return {new_node_data, source_target}; + } + + std::vector> stformat(const std::vector& new_path) { + std::vector> source_target; + size_t pathlen = new_path.size(); + + for (size_t n_edge = 0; n_edge < pathlen - 1; ++n_edge) { + source_target.emplace_back(new_path[n_edge], new_path[n_edge + 1], true); + } + + return source_target; + } + + std::vector> extract_nodes_data(pugi::xml_node root) { + std::vector> nodes_data; + + // Define the namespace URI + const char* ns = "http://graphml.graphdrawing.org/xmlns"; + + std::vector no_way_points; + std::vector no_way_points_second; + + for (pugi::xml_node node : root.children("node")) { + std::string node_id = node.attribute("id").value(); + double d0 = 0.0; + double d1 = 0.0; + bool d0_found = false, d1_found = false; + + for (pugi::xml_node data : node.children()) { + std::string key = data.attribute("key").value(); + if (key == "d0") { + d0 = data.text().as_double(); + d0_found = true; + } else if (key == "d1") { + d1 = data.text().as_double(); + d1_found = true; + } + } + + if (d0_found && d1_found && !node_id.empty()) { + nodes_data.emplace_back(node_id, d0, d1); + } else { + ROS_ERROR("Error: Incomplete or missing data for node id: %s", node_id.c_str()); + } + } + + return nodes_data; + } + + std::vector> extract_edges_data(pugi::xml_node root) { + std::vector> edges_data; + + for (pugi::xml_node edge : root.children("edge")) { + std::string source_id = edge.attribute("source").value(); + std::string target_id = edge.attribute("target").value(); + pugi::xml_node data_d2 = edge.find_child_by_attribute("data", "key", "d2"); + bool d2_value = (data_d2 && (std::string(data_d2.child_value()) == "True")); + + if (!source_id.empty() && !target_id.empty()) { + edges_data.emplace_back(source_id, target_id, d2_value); + } else { + ROS_ERROR("Error: Incomplete edge data source: %s target: %s", source_id.c_str(), target_id.c_str()); + } + } + return edges_data; + } + + std::pair, std::map>>> + extract_graph(const std::vector>& nodes_data_, + const std::vector>& edges_data_) { + + const double inf = std::numeric_limits::infinity(); + + std::map nodedict; + std::map>> edgedict; + + // Fill nodedict + for (const auto& node : nodes_data_) { + bool pass_through_current_node = false; + for (const auto& delete_node : edges_data_) { + if (std::get<0>(delete_node) == std::get<0>(node)) { + pass_through_current_node = !std::get<1>(delete_node).empty(); + break; + } + } + nodedict[std::get<0>(node)] = {inf, {}, pass_through_current_node, std::get<1>(node), std::get<2>(node)}; + } + + // Fill edgedict + for (const auto& edge : edges_data_) { + std::string source = std::get<0>(edge); + std::string target = std::get<1>(edge); + bool d2_value = std::get<2>(edge); + + if (edgedict.find(source) != edgedict.end()) { + edgedict[source].emplace_back(target, d2_value); + } else { + edgedict[source] = {{target, d2_value}}; + } + } + + return {nodedict, edgedict}; + } + +}; + diff --git a/src/example/include/mekatronom/utilities/mpc_running.h b/src/example/include/mekatronom/utilities/mpc_running.h new file mode 100644 index 0000000..64032fb --- /dev/null +++ b/src/example/include/mekatronom/utilities/mpc_running.h @@ -0,0 +1,641 @@ +/* +* MIT License +* +* Copyright (c) 2024 Mehmet Baha Dursun +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +/* + * Author: Mehmet Baha Dursun + */ + +#pragma once + +#include "mekatronom/MpcNode.hpp" + + +class MpcRunning +{ + +public: + MpcRunning(MpcNode& node) + { + try { + + ROS_INFO_ONCE("MpcRunning constructor"); + + node.mpc_setting_outputs_.args["p"] = vertcat( + node.mpc_setting_outputs_.state_init, + node.mpc_setting_outputs_.state_target + ); + node.mpc_setting_outputs_.args["x0"] = vertcat( + reshape(node.mpc_setting_outputs_.X0.T(), node.mpc_setting_outputs_.n_states * (node.initial_settings_.N + 1), 1), + reshape(node.mpc_setting_outputs_.u0.T(), node.mpc_setting_outputs_.n_controls * node.initial_settings_.N, 1) + ); + + // std::cout << "Solver setup: " << node.mpc_setting_outputs_.solver << std::endl; + // // Debug values + // std::cout << "x0 values: " << node.mpc_setting_outputs_.args["x0"] << std::endl; + // std::cout << "lbx values: " << node.mpc_setting_outputs_.args["lbx"] << std::endl; + // std::cout << "ubx values: " << node.mpc_setting_outputs_.args["ubx"] << std::endl; + // std::cout << "lbg values: " << node.mpc_setting_outputs_.args["lbg"] << std::endl; + // std::cout << "ubg values: " << node.mpc_setting_outputs_.args["ubg"] << std::endl; + // std::cout << "p values: " << node.mpc_setting_outputs_.args["p"] << std::endl; + + // Example initialization of CasADi DM matrices + DM x0 = node.mpc_setting_outputs_.args["x0"]; + casadi::DM lbx = node.mpc_setting_outputs_.args["lbx"]; + casadi::DM ubx = node.mpc_setting_outputs_.args["ubx"]; + casadi::DM lbg = node.mpc_setting_outputs_.args["lbg"]; + casadi::DM ubg = node.mpc_setting_outputs_.args["ubg"]; + casadi::DM p = node.mpc_setting_outputs_.args["p"]; + + + // Check for any unexpected negative sizes + if (x0.size1() < 0 || x0.size2() < 0 || + lbx.size1() < 0 || lbx.size2() < 0 || + ubx.size1() < 0 || ubx.size2() < 0 || + lbg.size1() < 0 || lbg.size2() < 0 || + ubg.size1() < 0 || ubg.size2() < 0 || + p.size1() < 0 || p.size2() < 0) { + std::cerr << "Error: Negative dimensions detected!" << std::endl; + } + + // Prepare the solver arguments + DMDict args = { + {"x0", node.mpc_setting_outputs_.args["x0"]}, + {"lbx", node.mpc_setting_outputs_.args["lbx"]}, + {"ubx", node.mpc_setting_outputs_.args["ubx"]}, + {"lbg", node.mpc_setting_outputs_.args["lbg"]}, + {"ubg", node.mpc_setting_outputs_.args["ubg"]}, + {"p", node.mpc_setting_outputs_.args["p"]} + }; + + // Execute the solver + DMDict sol; + try { + sol = node.mpc_setting_outputs_.solver(args); + std::cout << "Solver executed successfully." << std::endl; + } catch (const std::exception& e) { + std::cerr << "Solver error: " << e.what() << std::endl; + return; + } + + DM u_portion = sol["x"](Slice(node.mpc_setting_outputs_.n_states * (node.initial_settings_.N + 1), Slice().stop)); + node.mpc_setting_outputs_.u = reshape(u_portion.T(), node.mpc_setting_outputs_.n_controls, node.initial_settings_.N).T(); + + std::cout << "node.mpc_setting_outputs_.state_target" << node.mpc_setting_outputs_.state_target << std::endl; + + shiftTimestep(node); + + node.mpc_setting_outputs_.u0 = vertcat( + node.mpc_setting_outputs_.u(Slice(1, node.mpc_setting_outputs_.u.rows()), Slice()), + node.mpc_setting_outputs_.u(Slice(-1), Slice()) + ); + + // std::cout <<"u0 output"<< node.mpc_setting_outputs_.u0<> matching_pairs = isTargetNodePresent(node, closest_node_id); + updateNextTargetNode(node, matching_pairs, closest_node_id, closest_node_id_original); + + } catch (const std::exception& e) { + std::cerr << "Solver error: " << e.what() << std::endl; + return; + } + } + + /* + * if there is no matching_pairs its probably last node. So we are accepting as that and giving always same node + * So if it solve currectly newer try new solutions. + * else we are updating the next target node. The purpose of the pi< yaw thing is while robot is moving always the next yaw need to be change. + * And because of we are using Ecluidian system while the limit of 360 to 0 there is no solution. For this we are always referencing one side + */ + void updateNextTargetNode (MpcNode& node, const std::vector>& matching_pairs, + const std::string& closest_node_id, const std::string& closest_node_id_original) { + if (matching_pairs.empty()) + { + DM dm_distance = DM::ones(1); + DM state_target_slice = node.mpc_setting_outputs_.state_target(Slice(0, 3)); + DM state_init_slice = node.mpc_setting_outputs_.state_init(Slice(0, 3)); + + double distance = static_cast(norm_2(state_target_slice - state_init_slice)(0)); + if (distance < 0.15) // its stable for my tuned parameters, will be check this later + { + node.mpc_setting_outputs_.state_target = DM({node.localisation_data_.x, node.localisation_data_.y, node.localisation_data_.yaw}); + } + node.last_update_time_.watchdogTimer = ros::Time::now().toSec(); + node.distance_flag_ = false; + } + else + { + double bandwith = M_PI / 2; + + std::string next_id = matching_pairs[0].second; + + auto matching_entry_opt = findMatchingEntry(node.djikstra_outputs_.pathGoalsYawDegree, next_id); + + + if (matching_entry_opt.has_value()) { + + auto matching_entry = matching_entry_opt.value(); + + std::cout << "\nMatching entry found for next node id: " << matching_entry << std::endl; + + auto target_x = std::get<1>(matching_entry); + auto target_y = std::get<2>(matching_entry); + auto target_yaw = std::get<3>(matching_entry); + + double dx = target_x - node.localisation_data_.x; + double dy = target_y - node.localisation_data_.y; + double yaw = std::atan2(dy, dx); + + if (yaw > M_PI) { + yaw -= 2 * M_PI; + } else if (yaw < -M_PI) { + yaw += 2 * M_PI; + } + + if (node.localisation_data_.yaw > M_PI) { + node.localisation_data_.yaw -= 2 * M_PI; + } else if (node.localisation_data_.yaw < -M_PI) { + node.localisation_data_.yaw += 2 * M_PI; + } + + if ((node.localisation_data_.yaw + bandwith > M_PI) && (yaw < -bandwith)) { + double cars_value = M_PI - node.localisation_data_.yaw; + double goal_value = M_PI - abs(yaw); + yaw = cars_value + goal_value + node.localisation_data_.yaw; + } + if ((node.localisation_data_.yaw - bandwith < -M_PI) && (yaw > bandwith)) { + double goal_value = M_PI - yaw; + yaw = -M_PI - goal_value; + } + + std::cout << "Next Step ID: " << next_id << "X: " << target_x << "Y: " << target_y << "Yaw: " << yaw << std::endl; + + node.last_update_time_.went_node = ros::Time::now().toSec(); + + updateStateTarget(node, next_id, target_x, target_y, yaw); + + node.goal_id_ = matching_entry; + if (node.car_behaviour_state_ == "keep_lane") + { + node.last_update_time_.watchdogTimer = ros::Time::now().toSec(); + } + } + else { + node.distance_flag_ = false; + node.mpc_setting_outputs_.state_target = DM({node.localisation_data_.x, node.localisation_data_.y, node.localisation_data_.yaw}); + node.last_update_time_.watchdogTimer = ros::Time::now().toSec(); + ROS_ERROR("Matching entry not found for next node id: %s", next_id.c_str()); + } + } + } + + /* + * If the next node is parking spot, the robot should go to the parking spot and stop. + * we are equaling the yaw to 0.0 because the robot should go straight to the parking spot. IN OUR CASE PARKING SPOT IS ALWAYS IN THE X AXIS + * Its dirty i know. But we saved the day with like this. + */ + void updateStateTarget(MpcNode& node, const std::string& next_id, double target_x, double target_y, double yaw) { + if (std::find(node.initial_settings_.parking_nodes_id.begin(), + node.initial_settings_.parking_nodes_id.end(), next_id) != + node.initial_settings_.parking_nodes_id.end()) { + node.mpc_setting_outputs_.state_target = DM({target_x, target_y, 0.0}); + node.distance_flag_ = false; + } else { + node.mpc_setting_outputs_.state_target = DM({target_x, target_y, yaw}); + std::cout << "\nNext node is not a parking spot." << std::endl; + } + } + + std::vector> isTargetNodePresent(MpcNode& node, const std::string& closest_node_id) { + std::vector> matching_pairs; + + for (const auto& p : node.djikstra_outputs_.SourceTargetNodes) { + if (p.first == closest_node_id) { + matching_pairs.push_back(p); + } + } + + return matching_pairs; + } + + void carControlExecution(MpcNode& node) { + + std::cout << "\nnode.car_behaviour_state_: " << node.car_behaviour_state_ << std::endl; + if (node.car_behaviour_state_ == "keep_lane") + { + // Access the value at (0, 1) in the matrix and convert it to a double + double steerAngleValue = static_cast(node.mpc_setting_outputs_.u(0, 1)); + node.mpc_setting_outputs_.steerAngle = (180.0 / M_PI) * steerAngleValue; + std::cout << "steerAngleValue: " << steerAngleValue << std::endl; + // Access the value at (0, 0) in the matrix and convert it to a double + double steerLateralValue = static_cast(node.mpc_setting_outputs_.u(0, 0)); + node.mpc_setting_outputs_.steerLateral = steerLateralValue; + carControlPublisher(node); + } + + std::cout<< "node.mpc_setting_outputs_.steerAngle" << node.mpc_setting_outputs_.steerAngle << std::endl; + std::cout<< "node.mpc_setting_outputs_.steerLateral" << node.mpc_setting_outputs_.steerLateral << std::endl; + + } + + std::tuple updateAndProcessNearestWaypoint(MpcNode& node) { + + std::string closest_node_id; + std::string closest_node_id_original; + + DM dm_distance = DM::ones(1); + DM state_target_slice = node.mpc_setting_outputs_.state_target(Slice(0, 2)); + DM state_init_slice = node.mpc_setting_outputs_.state_init(Slice(0, 2)); + + double distance = static_cast(norm_2(state_target_slice - state_init_slice)(0)); + if (distance < node.initial_settings_.goal_checker) { + + + + if(node.djikstra_outputs_.pathGoalsYawDegree.size() > 0) { + closest_node_id = calculateClosestNodeId( node.djikstra_outputs_.pathGoalsYawDegree, + node.localisation_data_.x, node.localisation_data_.y); + } + else { + closest_node_id = calculateClosestNodeId(node.djikstra_outputs_.pathGoalsYawDegreeCopy, + node.localisation_data_.x, node.localisation_data_.y); + } + + closest_node_id_original = calculateClosestNodeId(node.djikstra_outputs_.pathGoalsYawDegreeOriginal, + node.localisation_data_.x, node.localisation_data_.y); + node.closest_node_id_original_ = closest_node_id_original; // TODO: dirty will fix it later + node.closest_node_id_ = closest_node_id; // TODO: dirty will fix it later + + updatePassThrough(node, closest_node_id_original); + processObstacles(node,closest_node_id_original); + + } + return {closest_node_id, closest_node_id_original}; + } + + void updatePassThrough(MpcNode& node, const std::string& closest_node_id_original) { + std::string current_data = closest_node_id_original; + auto& node_info = node.djikstra_outputs_.node_dict[current_data]; + + if (node_info.pass_through) { + node.djikstra_outputs_.pass_through = true; + } else { + node.djikstra_outputs_.pass_through = false; + } + } + + void processObstacles(MpcNode& node, const std::string& closest_node_id_original) { + std::vector sign_looking_band; + std::string current_id_for_obstacles = calculateClosestNodeId(node.djikstra_outputs_.pathGoalsYawDegreeCopy, + node.localisation_data_.x, node.localisation_data_.y); + + for (int i = 1; i <= 5; ++i) { + std::cout << "Processing obstacle nodes for iteration " << i << std::endl; + std::vector> matching_pairs_signs = findMatchingPairs(node.djikstra_outputs_.SourceTargetNodesCopy, current_id_for_obstacles); + + std::tuple matching_entry; + std::tuple matching_entry_second; + + + auto matching_entry_opt = findMatchingEntry(node.djikstra_outputs_.pathGoalsYawDegreeCopy, current_id_for_obstacles); + + if (matching_entry_opt) { + matching_entry = matching_entry_opt.value(); + } else { + ROS_ERROR("Matching entry not found for current node id: %s", current_id_for_obstacles.c_str()); + } + + if (!matching_pairs_signs.empty()) { + std::cout << "\nMatching pairs found for current node id: " << current_id_for_obstacles << std::endl; + std::string next_id_signs = matching_pairs_signs[0].second; + + auto matching_entry_second_opt = findMatchingEntry(node.djikstra_outputs_.pathGoalsYawDegreeCopy, next_id_signs); + if (matching_entry_second_opt) { + matching_entry_second = matching_entry_second_opt.value(); + } else { + ROS_ERROR("Matching entry not found for next node id: %s", next_id_signs.c_str()); + } + + sign_looking_band.push_back(current_id_for_obstacles); + current_id_for_obstacles = next_id_signs; + + std::array target_position = {std::get<1>(matching_entry), std::get<2>(matching_entry)}; + double x_diff = std::abs(std::get<1>(matching_entry) - std::get<1>(matching_entry_second)); + double y_diff = std::abs(std::get<2>(matching_entry) - std::get<2>(matching_entry_second)); + + double x_thereshold = 0.1; + double y_thereshold = 0.1; + + findingObstacleNodes(node, closest_node_id_original, matching_entry, matching_entry_second, target_position, x_thereshold, y_thereshold); + } + } + } + + void findingObstacleNodes(MpcNode& node, const std::string& closest_node_id_original, + const std::tuple& matching_entry, + const std::tuple& matching_entry_second, + const std::array& target_position, + double x_thereshold, double y_thereshold) + { + Eigen::Vector2d targetVec(target_position[0], target_position[1]); + + for (size_t i = 0; i < node.center_x_.size(); ++i) { + Eigen::Vector2d obstacleVec(node.center_x_[i], node.center_y_[i]); + + std::cout << "\nChecking obstacle proximity for obstacle at (" << node.center_x_[i] << ", " << node.center_y_[i] << ")" << std::endl; + std::cout << "\nTarget position: (" << target_position[0] << ", " << target_position[1] << ")" << std::endl; + + for (const auto& [node_id, coordinates] : node.djikstra_outputs_.obstacle_node_positions) { + double x = coordinates.first; + double y = coordinates.second; + double distance = std::sqrt(std::pow(node.center_x_[i] - x, 2) + std::pow(node.center_y_[i] - y, 2)); + + if (distance <= 0.4 && std::find(node.initial_settings_.parking_spot_is_full.begin(), + node.initial_settings_.parking_spot_is_full.end(), node_id) == node.initial_settings_.parking_spot_is_full.end()) { + + node.initial_settings_.parking_spot_is_full.push_back(node_id); + node.initial_settings_.parking_nodes_id.erase(std::remove(node.initial_settings_.parking_nodes_id.begin(), + node.initial_settings_.parking_nodes_id.end(), node_id), + node.initial_settings_.parking_nodes_id.end()); + } + } + + if (std::find(node.initial_settings_.parking_spot_is_full.begin(), + node.initial_settings_.parking_spot_is_full.end(), + node.initial_settings_.target_node) != node.initial_settings_.parking_spot_is_full.end()) + { + std::cout << "Target node is a parking spot. Updating target node to the next parking spot" << std::endl; + node.initial_settings_.target_node = node.initial_settings_.parking_nodes_id[0]; + processAndPublishPath processAndPublishPath(node.graphml_file_path_, closest_node_id_original, node.initial_settings_.target_node, node); + } + + // Calculate distance to the target position + double distance = (obstacleVec - targetVec).norm(); + + // Use std::get<> to access tuple elements + double x1 = std::get<1>(matching_entry); + double y1 = std::get<2>(matching_entry); + double x2 = std::get<1>(matching_entry_second); + double y2 = std::get<2>(matching_entry_second); + + // Check if within thresholds + bool is_within_x_threshold = std::min(x1, x2) - x_thereshold <= node.center_x_[i] && + node.center_x_[i] <= std::max(x1, x2) + x_thereshold; + bool is_within_y_threshold = std::min(y1, y2) - y_thereshold <= node.center_y_[i] && + node.center_y_[i] <= std::max(y1, y2) + y_thereshold; + + int entry_id = std::get<0>(matching_entry); + std::string entry_id_str = std::to_string(entry_id); + + checkingObstacleProximity(node, distance, is_within_x_threshold, is_within_y_threshold, entry_id_str, matching_entry, matching_entry_second, closest_node_id_original); + updateAvailableParkingSpots(node); + IsObstacleStillThere(node, obstacleVec); + TrafficSignManager TrafficSignManager(node); + } + } + void checkingObstacleProximity(MpcNode& node, double distance, bool is_within_x_threshold, bool is_within_y_threshold, const std::string& entry_id_str, + const std::tuple& matching_entry, + const std::tuple& matching_entry_second, + const std::string& closest_node_id_original) + { + if ((distance < 0.15 || (is_within_x_threshold && is_within_y_threshold)) && + (std::find(node.initial_settings_.dont_check_obstacles_this_nodes.begin(), + node.initial_settings_.dont_check_obstacles_this_nodes.end(), + entry_id_str) == node.initial_settings_.dont_check_obstacles_this_nodes.end())) + { + std::cout << "Obstacle is close or within thresholds" << std::endl; + + std::vector past_excluded_nodes = node.initial_settings_.excluded_nodes; + + // Convert tuple entries to strings + std::string matching_entry_second_id_str = std::to_string(std::get<0>(matching_entry_second)); + + // Check and update the obstacles array + if (std::find(node.initial_settings_.obstacles_array.begin(), + node.initial_settings_.obstacles_array.end(), entry_id_str) == node.initial_settings_.obstacles_array.end()) + { + node.initial_settings_.obstacles_array.push_back(entry_id_str); + } + + // Check if the second matching entry should be added to obstacles array + if (std::find(node.initial_settings_.excluded_nodes.begin(), + node.initial_settings_.excluded_nodes.end(), matching_entry_second_id_str) == node.initial_settings_.excluded_nodes.end()) + { + node.initial_settings_.obstacles_array.push_back(matching_entry_second_id_str); + } + + // Add obstacles to the excluded nodes if not already present + for (const auto& obstacle_id : node.initial_settings_.obstacles_array) { + // No need to convert obstacle_id if it's already a std::string + if (std::find(node.initial_settings_.excluded_nodes.begin(), + node.initial_settings_.excluded_nodes.end(), obstacle_id) == node.initial_settings_.excluded_nodes.end()) + { + node.initial_settings_.excluded_nodes.push_back(obstacle_id); + } + } + + // Check if excluded nodes have changed and update if necessary + if (node.initial_settings_.excluded_nodes != past_excluded_nodes) { + std::cout << "\n\nExcluded nodes have changed. Updating Djikstra" << std::endl; + std::cout << "closest_node_id_original: " << closest_node_id_original << std::endl; + std::cout << "node.initial_settings_.target_node: " << node.initial_settings_.target_node << std::endl; + std::cout << "node.initial_settings_.excluded_nodes: " << node.initial_settings_.excluded_nodes << std::endl; + processAndPublishPath processAndPublishPath(node.graphml_file_path_, closest_node_id_original, node.initial_settings_.target_node, node); + node.last_update_time_.obstacles_checking = ros::Time::now().toSec(); + } + else { + std::cout << "Found obstacle is already in the excluded nodes list" << std::endl; + } + + } + } + + std::vector> findMatchingPairs(const std::vector>& sourceTargetNodes, const std::string& current_id) { + std::vector> matching_pairs; + for (const auto& pair : sourceTargetNodes) { + if (pair.first == current_id) { + matching_pairs.push_back(pair); + } + } + return matching_pairs; + } + + std::optional> findMatchingEntry( + const std::vector>& pathGoalsYawDegree, + const std::string& current_id) { + + for (const auto& entry : pathGoalsYawDegree) { + if (std::to_string(std::get<0>(entry)) == current_id) { + return entry; // Return the found tuple + } + } + return std::nullopt; // Return std::nullopt when not found + } + + std::string calculateClosestNodeId( + const std::vector>& pathGoalsYawDegree, + double position_x, double position_y) + { + if (pathGoalsYawDegree.empty()) { + throw std::runtime_error("pathGoalsYawDegree is empty"); + } + + std::vector nodes_x; + std::vector nodes_y; + + for (const auto& node : pathGoalsYawDegree) { + nodes_x.push_back(std::get<1>(node)); + nodes_y.push_back(std::get<2>(node)); + } + + std::vector distances; + for (size_t i = 0; i < nodes_x.size(); ++i) { + double distance = std::sqrt(std::pow(nodes_x[i] - position_x, 2) + std::pow(nodes_y[i] - position_y, 2)); + distances.push_back(distance); + } + + auto min_it = std::min_element(distances.begin(), distances.end()); + size_t last_path_index = std::distance(distances.begin(), min_it); + return std::to_string(std::get<0>(pathGoalsYawDegree[last_path_index])); + } + + void updateAvailableParkingSpots(MpcNode& node) { + std::set_difference(node.initial_settings_.parking_nodes_id.begin(), node.initial_settings_.parking_nodes_id.end(), + node.initial_settings_.parking_spot_is_full.begin(), node.initial_settings_.parking_spot_is_full.end(), + std::back_inserter(node.initial_settings_.parkings_are_available)); + } + + void IsObstacleStillThere(MpcNode& node, const Eigen::Vector2d& obstacle_position) { + for (const auto& obstacle_id : node.initial_settings_.obstacles_array) { + auto matching_entry_obstacle_first = std::find_if(node.djikstra_outputs_.pathGoalsYawDegreeCopy.begin(), + node.djikstra_outputs_.pathGoalsYawDegreeCopy.end(), + [&obstacle_id](const std::tuple& entry) { + return std::to_string(std::get<0>(entry)) == obstacle_id; + }); + auto matching_entry_id_second =std::find_if(node.djikstra_outputs_.SourceTargetNodesCopy.begin(), + node.djikstra_outputs_.SourceTargetNodesCopy.end(), + [&obstacle_id](const std::pair& pair) { + return pair.first == obstacle_id; + }); + + auto matching_entry_obstacle_second = std::find_if(node.djikstra_outputs_.pathGoalsYawDegreeCopy.begin(), + node.djikstra_outputs_.pathGoalsYawDegreeCopy.end(), + [&matching_entry_id_second](const std::tuple& entry) { + return std::to_string(std::get<0>(entry)) == matching_entry_id_second->second; + }); + + double x_thereshold = 0.15; + double y_thereshold = 0.15; + + Eigen::Vector2d target_position_obstacle_first(std::get<1>(*matching_entry_obstacle_first), std::get<2>(*matching_entry_obstacle_first)); + Eigen::Vector2d target_position_obstacle_second(std::get<1>(*matching_entry_obstacle_second), std::get<2>(*matching_entry_obstacle_second)); + + bool is_within_x_threshold = std::min(target_position_obstacle_first[0], target_position_obstacle_second[0]) - x_thereshold <= node.center_x_[0] && + node.center_x_[0] <= std::max(target_position_obstacle_first[0], target_position_obstacle_second[0]) + x_thereshold; + + bool is_within_y_threshold = std::min(target_position_obstacle_first[1], target_position_obstacle_second[1]) - y_thereshold <= node.center_y_[0] && + node.center_y_[0] <= std::max(target_position_obstacle_first[1], target_position_obstacle_second[1]) + y_thereshold; + + double norm_first = (target_position_obstacle_first - obstacle_position).norm(); + double norm_second = (target_position_obstacle_second - obstacle_position).norm(); + + if (norm_first < 0.16 || norm_second < 0.16 || (is_within_x_threshold && is_within_y_threshold)) { + node.car_behaviour_state_ = "waiting the obstacle move away"; + node.checking_counter_ = 0; + node.last_update_time_.obstacles_checking = ros::Time::now().toSec(); + } + } + } + + void shiftTimestep(MpcNode& node) { + // Initialize state_init with the current position and orientation + node.mpc_setting_outputs_.state_init = DM(std::vector{ + node.localisation_data_.x, + node.localisation_data_.y, + node.localisation_data_.yaw + }); + + // Prepare the inputs for the CasADi function + std::vector f_inputs = { + node.mpc_setting_outputs_.state_init, + node.mpc_setting_outputs_.u(Slice(0), Slice()).T() + }; + + // Call the function and compute next_state (assuming f returns a vector of DM) + std::vector f_outputs = node.mpc_setting_outputs_.f(f_inputs); + DM f_value = f_outputs[0]; + + // Update the next state + node.mpc_setting_outputs_.next_state = node.mpc_setting_outputs_.state_init + + (node.initial_settings_.step_horizon * f_value); + + } + + void carControlPublisher(MpcNode& node) { + // Create JSON object for car_steer_angle_data + json car_steer_angle_data = { + {"action", "2"}, + {"steerAngle", node.mpc_setting_outputs_.steerAngle} + }; + std::string car_steer_angle_data_to_JSON = car_steer_angle_data.dump(); + + // Create a std_msgs::String message for car_steer_angle_data + std_msgs::String car_steer_angle_msg; + car_steer_angle_msg.data = car_steer_angle_data_to_JSON; + + // Publish carData + node.carControl_pub_.publish(car_steer_angle_msg); + + // Create JSON object for car_steer_lateral_data + json car_steer_lateral_data = { + {"action", "1"}, + {"speed", node.mpc_setting_outputs_.steerLateral} + }; + std::string car_steer_lateral_data_to_JSON = car_steer_lateral_data.dump(); + + // Create a std_msgs::String message for car_steer_lateral_data + std_msgs::String car_steer_lateral_msg; + car_steer_lateral_msg.data = car_steer_lateral_data_to_JSON; + + // Publish car2Data + node.carControl_pub_.publish(car_steer_lateral_msg); + } + +}; \ No newline at end of file diff --git a/src/example/include/mekatronom/utilities/mpc_start_setting.h b/src/example/include/mekatronom/utilities/mpc_start_setting.h new file mode 100644 index 0000000..bc7b625 --- /dev/null +++ b/src/example/include/mekatronom/utilities/mpc_start_setting.h @@ -0,0 +1,310 @@ +/* +* MIT License +* +* Copyright (c) 2024 Mehmet Baha Dursun +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +/* + * Author: Mehmet Baha Dursun + */ + +#pragma once + +#include "mekatronom/MpcNode.hpp" + +// Scenario names as constants +const std::string SCENARIO_PARK = "park"; +const std::string SCENARIO_CROSSWALK = "crosswalk"; +const std::string SCENARIO_MOTORWAY = "motorway"; +const std::string SCENARIO_INITIAL = "initial_settings"; + +class MpcStartSetting +{ +public: + MpcStartSetting(const std::string& scenerio_name_, MpcNode& node) + { + ROS_INFO("scenerio_name: %s", scenerio_name_.c_str()); + setlocale(LC_NUMERIC, "C"); + + /* + * Try function is bit cheating. Those parameters tuned in real life scenario. + * So if anyone want to tune it he can change the values in the settings. + * This values following the order: + * Qx,Qy,Qtheta,R1,R2,step_horizon,N,rob_diam,wheel_radius,L_value,Ly,v_max,v_min,omega_max,omega_min + */ + try { + + MpcNode::Settings initial_settings{ + 1.0, 1.0, 1.0, 1.0, 1.8, 0.2, 12, 0.354, 1.0, 0.4, 0.04, 0.33,-0.1, M_PI / 7.5, -M_PI / 7.5 + }; + MpcNode::Settings park_scenerio_settings{ + 1.0, 4.0, 0.05, 0.01, 0.02, 0.1, 6, 0.354, 1., 0.3, 0.04, 0.2,-0.25, M_PI / 7.5, -M_PI / 7.5 + }; + MpcNode::Settings crosswalk_scenerio_settings{ + 1.0, 1.0, 1., 1., 1.8, 0.1, 12, 0.354, 1., 0.3, 0.04, 0.13,-0.2, M_PI / 7.5, -M_PI / 7.5 + }; + MpcNode::Settings motorway_scenerio_settings{ + 1.0, 1.0, 2., 2., 2.8, 0.2, 4, 0.354, 1., 0.5, 0.04, 0.5,-0.1, M_PI / 7.5, -M_PI / 7.5 + }; + + + if (scenerio_name_ == SCENARIO_PARK) { + ROS_INFO("The scenario is park."); + node.initial_settings_ = park_scenerio_settings; + } else if (scenerio_name_ == SCENARIO_CROSSWALK) { + ROS_INFO("The scenario is crosswalk."); + node.initial_settings_ = crosswalk_scenerio_settings; + } else if (scenerio_name_ == SCENARIO_MOTORWAY) { + ROS_INFO("The scenario is motorway."); + node.initial_settings_ = motorway_scenerio_settings; + } else if (scenerio_name_ == SCENARIO_INITIAL) { + ROS_INFO("The scenario is initial settings."); + node.initial_settings_ = initial_settings; + } else { + ROS_INFO("The scenario is not recognized. The default settings are set."); + + } + + } catch (const std::exception& e) { + ROS_FATAL("Exception caught during setting the scenario: %s", e.what()); + return; + } + + + node.mpc_setting_outputs_.state_init = DM::vertcat({node.localisation_data_.x, node.localisation_data_.y, node.localisation_data_.yaw}); + + std::vector nodes_x; + std::vector nodes_y; + + for (const auto& node : node.djikstra_outputs_.pathGoalsYawDegreeCopy){ + nodes_x.push_back(std::get<1>(node)); + nodes_y.push_back(std::get<2>(node)); + } + + std::vector distances; + for (size_t i =0;i(node.djikstra_outputs_.pathGoalsYawDegreeCopy[index]); + node.mpc_setting_outputs_.last_path_index = index; + + // Finding the next node + std::pair matching_pair; + for (const auto& pair : node.djikstra_outputs_.SourceTargetNodesCopy) { + if (pair.first == std::to_string(closest_node_id)) { + matching_pair = pair; + break; + } + } + + std::string next_node_id_str = matching_pair.second; + int next_node_id = std::stoi(next_node_id_str); // Convert string back to int + + std::tuple matching_entry; + bool matching_entry_found = false; + for (const auto& entry : node.djikstra_outputs_.pathGoalsYawDegreeCopy) { + if (std::get<0>(entry) == next_node_id) { + matching_entry = entry; + matching_entry_found = true; + break; + } + } + + if (!matching_entry_found) { + ROS_FATAL("Matching entry not found for next node id: %d", next_node_id); + return; + } else { + double target_x = std::get<1>(matching_entry); + double target_y = std::get<2>(matching_entry); + + double dx = target_x - node.localisation_data_.x; + double dy = target_y - node.localisation_data_.y; + double yaw = std::atan2(dy, dx); + + int goal_id = std::get<0>(matching_entry); + + node.mpc_setting_outputs_.state_init = DM({node.localisation_data_.x, node.localisation_data_.y, node.localisation_data_.yaw}); + node.mpc_setting_outputs_.state_target = DM({target_x, target_y, yaw}); + + std::cout << "state_init: " << node.mpc_setting_outputs_.state_init << std::endl; + std::cout << "state_target: " << node.mpc_setting_outputs_.state_target << std::endl; + } + + + /* + * Initial settings for the MPC + */ + SX x = SX::sym("x"); + SX y = SX::sym("y"); + SX theta = SX::sym("theta"); + SX states = vertcat(x, y, theta); + node.mpc_setting_outputs_.n_states = states.size1() * states.size2(); + + SX v = SX::sym("v"); + SX omega = SX::sym("omega"); + SX controls = vertcat(v, omega); + node.mpc_setting_outputs_.n_controls = controls.numel(); + + SX X = SX::sym("X", node.mpc_setting_outputs_.n_states, (node.initial_settings_.N + 1)); + SX U = SX::sym("U", node.mpc_setting_outputs_.n_controls, node.initial_settings_.N); + SX P = SX::sym("P", node.mpc_setting_outputs_.n_states + node.mpc_setting_outputs_.n_states); + + std::vector Q_elements; + Q_elements.push_back(SX::diag(node.initial_settings_.Q_x)); + Q_elements.push_back(SX::diag(node.initial_settings_.Q_y)); + Q_elements.push_back(SX::diag(node.initial_settings_.Q_theta)); + SX Q = diagcat(Q_elements); + + std::vector R_elements; + R_elements.push_back(SX::diag(node.initial_settings_.R1)); + R_elements.push_back(SX::diag(node.initial_settings_.R2)); + SX R = diagcat(R_elements); + + /* + * Bicycle model + */ + SX RHS = vertcat( + v * cos(theta), + v * sin(theta), + v / node.initial_settings_.L_value * tan(omega) + + ); + + node.mpc_setting_outputs_.f = Function("f", {states, controls}, {RHS}); + + SX cost_fn = 0; + SX g = X(Slice(), 0) - P(Slice(0, node.mpc_setting_outputs_.n_states)); + + SX st = X(Slice(), 0); + + /* + * Runge-Kutta 4th order integration method + */ + for (int k = 0; k < node.initial_settings_.N; ++k) { + SX st = X(Slice(), k); + SX con = U(Slice(), k); + std::cout << "st: " << st << std::endl; + std::cout << "con: " << con << std::endl; + + cost_fn += mtimes(mtimes((st - P(Slice(3, 6), 0)).T(), Q), (st - P(Slice(3, 6), 0))) + + mtimes(mtimes(con.T(), R), con); + + SX st_next = X(Slice(), k+1); + + auto k1 = node.mpc_setting_outputs_.f(std::vector{st, con}); + auto k2 = node.mpc_setting_outputs_.f(std::vector{st + (node.initial_settings_.step_horizon / 2) * k1.at(0), con}); + auto k3 = node.mpc_setting_outputs_.f(std::vector{st + (node.initial_settings_.step_horizon / 2) * k2.at(0), con}); + auto k4 = node.mpc_setting_outputs_.f(std::vector{st + node.initial_settings_.step_horizon * k3.at(0), con}); + + SX st_next_RK4 = st + (node.initial_settings_.step_horizon / 6) * (k1.at(0) + 2*k2.at(0) + 2*k3.at(0) + k4.at(0)); + // std::cout << "st_next: " << st_next << std::endl; + // std::cout << "st_next_RK4: " << st_next_RK4 << std::endl; + // std::cout << "test: " << std::endl; + g = vertcat(g, st_next - st_next_RK4); + // Verify that number of constraints matches expectations + // std::cout << "Number of constraints in g: " << g.size1() << std::endl; + } + + SX OPT_variables = vertcat(reshape(X, node.mpc_setting_outputs_.n_states * (node.initial_settings_.N + 1), 1), reshape(U, node.mpc_setting_outputs_.n_controls * node.initial_settings_.N, 1)); + // std::cout << "OPT_variables: " << OPT_variables << std::endl; + // std::cout << "cost_fn: " << cost_fn << std::endl; + // std::cout << "g: " << g << std::endl; + // std::cout << "P: " << P << std::endl; + SXDict nlp_prob = { + {"f", cost_fn}, + {"x", OPT_variables}, + {"g", g}, + {"p", P} + }; + + // std::cout << "nlp_prob: " << nlp_prob << std::endl; + Dict opts = { + {"ipopt.max_iter", 100}, + {"ipopt.print_level", 0}, + {"ipopt.acceptable_tol", 1e-8}, + {"ipopt.acceptable_obj_change_tol", 1e-6}, + {"print_time", 0}, + }; + + // std::cout << "opts: " << opts << std::endl; + + node.mpc_setting_outputs_.solver = nlpsol("solver", "ipopt", nlp_prob, opts); + + /* + * This section is for the constraints of the states and controls + * Currently supporting velocity and angular velocity constraints + */ + DM lbx = DM::zeros(node.mpc_setting_outputs_.n_states*(node.initial_settings_.N+1) + node.mpc_setting_outputs_.n_controls*node.initial_settings_.N, 1); + DM ubx = DM::zeros(node.mpc_setting_outputs_.n_states*(node.initial_settings_.N+1) + node.mpc_setting_outputs_.n_controls*node.initial_settings_.N, 1); + DM lbg = DM::zeros(node.mpc_setting_outputs_.n_states*(node.initial_settings_.N+1), 1); + DM ubg = DM::zeros(node.mpc_setting_outputs_.n_states*(node.initial_settings_.N+1), 1); + + // std::cout<< "node.mpc_setting_outputs_.solver: " << node.mpc_setting_outputs_.solver << std::endl; + + for (int i = 0; i < node.mpc_setting_outputs_.n_states * (node.initial_settings_.N + 1); i += node.mpc_setting_outputs_.n_states) { + // Assigning lower bounds + lbx(i) = -DM::inf(); // Ensure lbx is initialized correctly + lbx(i + 1) = -DM::inf(); + lbx(i + 2) = -DM::inf(); + + // Assigning upper bounds + ubx(i) = DM::inf(); // Ensure ubx is initialized correctly + ubx(i + 1) = DM::inf(); + ubx(i + 2) = DM::inf(); + } + + + for (int i = node.mpc_setting_outputs_.n_states*(node.initial_settings_.N+1); i < node.mpc_setting_outputs_.n_states*(node.initial_settings_.N+1)+2*node.initial_settings_.N; i+=2) { + lbx(i) = node.initial_settings_.v_min; + ubx(i) = node.initial_settings_.v_max; + lbx(i+1) = node.initial_settings_.omega_min; + ubx(i+1) = node.initial_settings_.omega_max; + + } + + + /* + * u0 is [velocity, angular velocity] + * x0 is [x, y, theta] + * Here is initial settings for the mpcRunning + */ + node.mpc_setting_outputs_.u0 = DM::zeros(node.initial_settings_.N, 2); + node.mpc_setting_outputs_.X0 = DM::repmat(node.mpc_setting_outputs_.state_init + , 1, node.initial_settings_.N+1).T(); + node.mpc_setting_outputs_.args["lbg"] = lbg; + node.mpc_setting_outputs_.args["ubg"] = ubg; + node.mpc_setting_outputs_.args["lbx"] = lbx; + node.mpc_setting_outputs_.args["ubx"] = ubx; + + // std::cout << "dual solution (x) = " << res.at("lbg") << std::endl; + node.mpc_setting_outputs_.cat_states = node.mpc_setting_outputs_.X0; + node.mpc_setting_outputs_.cat_controls = DM::zeros(0,node.mpc_setting_outputs_.u0.columns()); + // Check number of constraints + // std::cout << "Number of constraints in g: " << g.size1() << std::endl; + // std::cout << "Expected number of constraints: " << lbg.size1() << std::endl; + + } +}; \ No newline at end of file diff --git a/src/example/include/mekatronom/utilities/traffic_sign_manager.h b/src/example/include/mekatronom/utilities/traffic_sign_manager.h new file mode 100644 index 0000000..e513f02 --- /dev/null +++ b/src/example/include/mekatronom/utilities/traffic_sign_manager.h @@ -0,0 +1,69 @@ +/* +* MIT License +* +* Copyright (c) 2024 Mehmet Baha Dursun +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +/* + * Author: Mehmet Baha Dursun + */ + +#pragma once + +#include "mekatronom/MpcNode.hpp" + +class TrafficSignManager{ +public: + TrafficSignManager(MpcNode& node) + { + std::cout << "TrafficSignManager constructor" << std::endl; + + node.checking_counter_++; + + if ((node.car_behaviour_state_ == "keep_lane") && + (node.initial_settings_.obstacles_array.size() > 0) && + (node.checking_counter_ > 50) && + (node.djikstra_outputs_.pass_through == 0)) + { + std::cout << "Obstacle moved out of the way clearing the data" << std::endl; + for (const auto& value : node.initial_settings_.obstacles_array) { + auto obstacle_id = std::find(node.initial_settings_.excluded_nodes.begin(), node.initial_settings_.excluded_nodes.end(), value); + if (obstacle_id != node.initial_settings_.excluded_nodes.end()) { + node.initial_settings_.excluded_nodes.erase(obstacle_id); + } + } + node.initial_settings_.obstacles_array.clear(); + + processAndPublishPath processAndPublishPath(node.graphml_file_path_, node.closest_node_id_original_, node.initial_settings_.target_node, node); + } + + else + { + node.car_behaviour_state_ = "keep_lane"; + ROS_INFO("The car behaviour state is ", node.car_behaviour_state_); + } + + // TODO: For now other sections of parking motorway endofmotorway settings will be disabled. It does need yolo implementation. + // TODO: Might be I can run it with directly python script. IDK yet. + + } +}; + diff --git a/src/example/launch/bosch.launch b/src/example/launch/bosch.launch new file mode 100644 index 0000000..7856caa --- /dev/null +++ b/src/example/launch/bosch.launch @@ -0,0 +1,26 @@ + + + + + + + + + target_frame: "base_link" + transform_tolerance: 0.01 + + min_height: -0.12 + max_height: 0.4 + + angle_min: -0.54 + angle_max: 0.54 + angle_increment: 0.0087 + scan_time: 0.0333 + range_min: 0.3 + range_max: 2.5 + use_inf: true + concurrency_level: 1 + + + + \ No newline at end of file diff --git a/src/example/lib/djikstra/djikstra.cpp b/src/example/lib/djikstra/djikstra.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/example/lib/djikstra/include/djikstra/djikstra.h b/src/example/lib/djikstra/include/djikstra/djikstra.h new file mode 100644 index 0000000..e69de29 diff --git a/src/example/lib/mpc/include/mpc/mpc_start_setting2.h b/src/example/lib/mpc/include/mpc/mpc_start_setting2.h new file mode 100644 index 0000000..4834294 --- /dev/null +++ b/src/example/lib/mpc/include/mpc/mpc_start_setting2.h @@ -0,0 +1,81 @@ +/* +* MIT License +* +* Copyright (c) 2024 Mehmet Baha Dursun +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +/* + * Email: baha.dursun123@gmail.com + */ + +#pragma once + +#include +#include +#include + +using namespace casadi; + +// Scenario names as constants +const std::string SCENARIO_PARK = "park"; +const std::string SCENARIO_CROSSWALK = "crosswalk"; +const std::string SCENARIO_MOTORWAY = "motorway"; +const std::string SCENARIO_INITIAL = "initial_settings"; + +class MpcStartSetting { +public: + + typedef struct { + double Q_x; + double Q_y; + double Q_theta; + double R1; + double R2; + double step_horizon; + int N; + double rob_diam; + double wheel_radius; + double L_value; + double Ly; + double v_max; + double v_min; + double omega_max; + double omega_min; + int iteration; + } Settings; + + Settings initial_settings_; + + typedef struct { + double x; + double y; + double z; + double roll; + double pitch; + double yaw; + } Pose; + + Pose localisation_data_; + std::string scenerio_name_; + void mpc_start_setting(const std::string& scenerio_name_); + + +}; \ No newline at end of file diff --git a/src/example/lib/mpc/mpc_start_setting2.cpp b/src/example/lib/mpc/mpc_start_setting2.cpp new file mode 100644 index 0000000..53d1b5d --- /dev/null +++ b/src/example/lib/mpc/mpc_start_setting2.cpp @@ -0,0 +1,265 @@ +/* +* MIT License +* +* Copyright (c) 2024 Mehmet Baha Dursun +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +/* + * Author: Mehmet Baha Dursun + */ + +#include "mpc/mpc_start_setting2.h" + +void MpcStartSetting::mpc_start_setting(const std::string& scenerio_name_) +{ + ROS_INFO_ONCE("MpcRunning constructor"); + + ROS_INFO("scenerio_name: %s", scenerio_name_.c_str()); + setlocale(LC_NUMERIC, "C"); // Ensure '.' is used for decimal separation + + //TODO: Those settings tuned by hand for different scenerios. They should be tuned from here + + try { + + MpcStartSetting::Settings initial_settings{ + 1.0, 1.0, 1.0, 1.0, 1.8, 0.2, 12, 0.354, 1.0, 0.4, 0.04, 0.33,-0.1, M_PI / 7.5, -M_PI / 7.5 + }; + MpcStartSetting::Settings park_scenerio_settings{ + 1.0, 4.0, 0.05, 0.01, 0.02, 0.1, 6, 0.354, 1., 0.3, 0.04, 0.2,-0.25, M_PI / 7.5, -M_PI / 7.5 + }; + MpcStartSetting::Settings crosswalk_scenerio_settings{ + 1.0, 1.0, 1., 1., 1.8, 0.1, 12, 0.354, 1., 0.3, 0.04, 0.13,-0.2, M_PI / 7.5, -M_PI / 7.5 + }; + MpcStartSetting::Settings motorway_scenerio_settings{ + 1.0, 1.0, 2., 2., 2.8, 0.2, 4, 0.354, 1., 0.5, 0.04, 0.5,-0.1, M_PI / 7.5, -M_PI / 7.5 + }; + + + + if (scenerio_name_ == SCENARIO_PARK) { + ROS_INFO("The scenario is park."); + initial_settings_ = park_scenerio_settings; + } else if (scenerio_name_ == SCENARIO_CROSSWALK) { + ROS_INFO("The scenario is crosswalk."); + initial_settings_ = crosswalk_scenerio_settings; + } else if (scenerio_name_ == SCENARIO_MOTORWAY) { + ROS_INFO("The scenario is motorway."); + initial_settings_ = motorway_scenerio_settings; + } else if (scenerio_name_ == SCENARIO_INITIAL) { + ROS_INFO("The scenario is initial settings."); + initial_settings_ = initial_settings; + } else { + // Handle the case where scenerio_name_ is not recognized. + initial_settings_ = initial_settings; + ROS_INFO("The scenario is not recognized. The default settings are set."); + } + + ROS_INFO("The settings are set for the scenario: %f", initial_settings_.Q_x); + ROS_INFO("The settings are set for the scenario: %f", initial_settings_.Q_y); + ROS_INFO("The settings are set for the scenario: %f", initial_settings_.Q_theta); + ROS_INFO("The settings are set for the scenario: %f", initial_settings_.R1); + ROS_INFO("The settings are set for the scenario: %f", initial_settings_.R2); + ROS_INFO("The settings are set for the scenario: %f", initial_settings_.step_horizon); + ROS_INFO("The settings are set for the scenario: %d", initial_settings_.N); + ROS_INFO("The settings are set for the scenario: %f", initial_settings_.rob_diam); + ROS_INFO("The settings are set for the scenario: %f", initial_settings_.wheel_radius); + ROS_INFO("The settings are set for the scenario: %f", initial_settings_.L_value); + ROS_INFO("The settings are set for the scenario: %f", initial_settings_.Ly); + ROS_INFO("The settings are set for the scenario: %f", initial_settings_.v_max); + ROS_INFO("The settings are set for the scenario: %f", initial_settings_.v_min); + ROS_INFO("The settings are set for the scenario: %f", initial_settings_.omega_max); + ROS_INFO("The settings are set for the scenario: %f", initial_settings_.omega_min); + + } catch (const std::exception& e) { + ROS_FATAL("Exception caught during setting the scenario: %s", e.what()); + return; + } + + // // Dönüşüm matrisini oluştur + // Eigen::Matrix2d rotationMatrix; + + // rotationMatrix << std::cos(localisation_data_.yaw ), -std::sin(localisation_data_.yaw ), + // std::sin(localisation_data_.yaw ), std::cos(localisation_data_.yaw ); + + // ROS_INFO("imu_data_.yaw: %f", localisation_data_.yaw); + // // Dönüştürülecek matrisi tanımla + // Eigen::Vector2d matrix(1, 4); + + // // Dönüşümü uygula + // Eigen::Vector2d result = rotationMatrix * matrix; + + // Başlangıç ve hedef durumları tanımla + DM state_init = DM::vertcat({localisation_data_.x, localisation_data_.y, localisation_data_.yaw}); + DM state_target = DM::vertcat({12.09, 13.72-11.88, 0.0}); + + // Durum sembolik değişkenlerini tanımla + SX x = SX::sym("x"); + SX y = SX::sym("y"); + SX theta = SX::sym("theta"); + SX states = vertcat(x, y, theta); + int n_states = states.size1() * states.size2(); // numel yerine size1 ve size2 çarpımı kullanılır + std::cout << "n_states: " << n_states << std::endl; + + // new define + SX v = SX::sym("v"); + SX omega = SX::sym("omega"); + SX controls = vertcat(v, omega); + int n_controls = controls.numel(); + + // matrix containing all states over all time steps +1 (each column is a state vector) + SX X = SX::sym("X", n_states, (initial_settings_.N + 1)); + + // matrix containing all control actions over all time steps (each column is an action vector) + SX U = SX::sym("U", n_controls, initial_settings_.N); + + // column vector for storing initial state and target state + SX P = SX::sym("P", n_states + n_states); + + // state weights matrix (Q_X, Q_Y, Q_THETA) + std::vector Q_elements; + Q_elements.push_back(SX::diag(initial_settings_.Q_x)); // Assuming Q_x, Q_y, Q_theta are already defined SX expressions + Q_elements.push_back(SX::diag(initial_settings_.Q_y)); + Q_elements.push_back(SX::diag(initial_settings_.Q_theta)); + SX Q = diagcat(Q_elements); // Use vector for diagcat + + // Correct usage of diagcat for R as well + std::vector R_elements; + R_elements.push_back(SX::diag(initial_settings_.R1)); // Assuming R1, R2 are already defined SX expressions + R_elements.push_back(SX::diag(initial_settings_.R2)); + SX R = diagcat(R_elements); // Use vector for diagcat + + + // // discretization model (e.g. x2 = f(x1, v, t) = x1 + v * dt) + // SX rot_3d_z = vertcat( + // horzcat(cos(theta), -sin(theta), 0), + // horzcat(sin(theta), cos(theta), 0), + // horzcat(0, 0, 1) + // ); + + // // Mecanum wheel transfer function + // DM J = (wheel_radius / 4) * DM({ + // {1, 1, 1, 1}, + // {-1, 1, 1, -1}, + // {-1 / (Lx + Ly), 1 / (Lx + Ly), -1 / (Lx + Ly), 1 / (Lx + Ly)} + // }); + + SX RHS = vertcat( + v * cos(theta), + v * sin(theta), + omega + ); + + // maps controls from [va, vb, vc, vd].T to [vx, vy, omega].T + Function f = Function("f", {states, controls}, {RHS}); + + SX cost_fn = 0; // cost function + SX g = X(Slice(), 0) - P(Slice(0, n_states)); // constraints in the equation + + SX st = X(Slice(), 0); + std::cout << "st: " << st << std::endl; + std::cout << "g: " << g << std::endl; + + for (int k = 0; k < initial_settings_.N; ++k) { + SX st = X(Slice(), k); + SX con = U(Slice(), k); + + // Maliyet fonksiyonunu hesapla + cost_fn += mtimes(mtimes((st - P(Slice(3, 6), 0)).T(), Q), (st - P(Slice(3, 6), 0))) + + mtimes(mtimes(con.T(), R), con); + + // Bir sonraki durumu tahmin et + SX st_next = X(Slice(), k+1); + + auto k1 = f(std::vector{st, con}); + auto k2 = f(std::vector{st + (initial_settings_.step_horizon / 2) * k1.at(0), con}); + auto k3 = f(std::vector{st + (initial_settings_.step_horizon / 2) * k2.at(0), con}); + auto k4 = f(std::vector{st + initial_settings_.step_horizon * k3.at(0), con}); + + SX st_next_RK4 = st + (initial_settings_.step_horizon / 6) * (k1.at(0) + 2*k2.at(0) + 2*k3.at(0) + k4.at(0)); + + // Kısıtlama vektörünü güncelle + g = vertcat(g, st_next - st_next_RK4); + + } + std::cout << "cost_fn: " << cost_fn << std::endl; + std::cout << "g: " << g << std::endl; + + SX OPT_variables = vertcat(reshape(X, n_states * (initial_settings_.N + 1), 1), reshape(U, n_controls * initial_settings_.N, 1)); + + // nlp_prob sözlüğünü tanımlama + SXDict nlp_prob = { + {"f", cost_fn}, + {"x", OPT_variables}, + {"g", g}, + {"p", P} + }; + // Solver ayarlarını tanımlama + Dict opts = { + {"ipopt.max_iter", 100}, + {"ipopt.print_level", 0}, + {"ipopt.acceptable_tol", 1e-8}, + {"ipopt.acceptable_obj_change_tol", 1e-6}, + {"print_time", 0} + }; + + + Function solver = nlpsol("solver", "ipopt", nlp_prob, opts); + std::cout << "Solver " << solver << std::endl; + + DM lbx = DM::zeros(n_states*(initial_settings_.N+1) + n_controls*initial_settings_.N, 1); + DM ubx = DM::zeros(n_states*(initial_settings_.N+1) + n_controls*initial_settings_.N, 1); + DM lbg = DM::zeros(n_states*(initial_settings_.N+1), 1); + DM ubg = DM::zeros(n_states*(initial_settings_.N+1), 1); + + // Sınırları ayarlama + for (int i = 0; i < n_states*(initial_settings_.N+1); i+=n_states) { + lbx(i) = -DM::inf(); + lbx(i+1) = -DM::inf(); + lbx(i+2) = -DM::inf(); + + ubx(i) = DM::inf(); + ubx(i+1) = DM::inf(); + ubx(i+2) = DM::inf(); + } + + for (int i = n_states*(initial_settings_.N+1); i < n_states*(initial_settings_.N+1)+2*initial_settings_.N; i+=2) { + lbx(i) = initial_settings_.v_min; + ubx(i) = initial_settings_.v_max; + lbx(i+1) = initial_settings_.omega_min; + ubx(i+1) = initial_settings_.omega_max; + } + + // İlk durum ve kontrol değerleri + + DM t0 = 0; + DM u0 = DM::zeros(initial_settings_.N, 2); // İlk kontrol vektörü + DM X0 = DM::repmat(state_init, 1, initial_settings_.N+1).T(); // İlk durum vektörü + + // Args sözlüğü oluşturma + std::map args; + args["lbg"] = lbg; + args["ubg"] = ubg; + args["lbx"] = lbx; + args["ubx"] = ubx; + + std::cout << "args: " << args << std::endl; + +} \ No newline at end of file diff --git a/src/example/msg/MekatronomYolo.msg b/src/example/msg/MekatronomYolo.msg new file mode 100644 index 0000000..49e1763 --- /dev/null +++ b/src/example/msg/MekatronomYolo.msg @@ -0,0 +1,9 @@ +string[] object +string[] side +float32[] distance +float32[] depth +float32[] size +float32[] coordinates +string[] carside +string[] turn +float64[] constants \ No newline at end of file diff --git a/src/example/msg/carcontrol.msg b/src/example/msg/carcontrol.msg new file mode 100644 index 0000000..c3b66c4 --- /dev/null +++ b/src/example/msg/carcontrol.msg @@ -0,0 +1,2 @@ +float32 steerLateral +float32 steerAngle \ No newline at end of file diff --git a/src/example/package.xml b/src/example/package.xml index 7827b2f..955af12 100644 --- a/src/example/package.xml +++ b/src/example/package.xml @@ -2,20 +2,44 @@ example 1.0.0 - The sim_pkg package - Rares Lemnariu + The example package + Rares Lemnariu + Baha Dursun Bosch ECC - catkin - rospy + + + sensor_msgs + sensor_msgs + sensor_msgs + + + geometry_msgs std_msgs + roscpp + rospy utils - rospy + + geometry_msgs std_msgs + roscpp + rospy utils - rospy + + geometry_msgs std_msgs + roscpp + rospy utils + message_generation + message_runtime + + + roscpp + rostest + + zed_wrapper + diff --git a/src/example/scripts/MpcNode.cpp b/src/example/scripts/MpcNode.cpp new file mode 100755 index 0000000..6cd72ab --- /dev/null +++ b/src/example/scripts/MpcNode.cpp @@ -0,0 +1,204 @@ +#include "mekatronom/MpcNode.hpp" + +// Constructor +MpcNode::MpcNode(ros::NodeHandle& nh, ros::NodeHandle& nh_local) : nh_(nh), nh_local_(nh_local) +{ + // Get all parameters + get_parameters(); + + // Subscribers + // image_sub_ = nh_.subscribe("automobile/image_raw", 1, &MpcNode::imageCb, this); + imu_sub_ = nh_.subscribe("automobile/IMU", 1, &MpcNode::imuCb, this); + localisation_sub_ = nh_.subscribe("automobile/localisation", 1, &MpcNode::localisationCb, this); + obstacle_detection_sub_ = nh_.subscribe("obstacles", 1, &MpcNode::obstacleDetectionCb, this); + carControl_pub_ = nh_.advertise("automobile/command", 2); + + // Publishers + // cmd_pub_ = nh_.advertise("bool_msg", 1); + + // Timer + control_timer_ = nh_.createTimer(ros::Duration(1.0 / control_rate_), &MpcNode::controlCb, this); +} + +// Destructor +MpcNode::~MpcNode() +{ + + ros::shutdown(); +} + +void MpcNode::controlCb(const ros::TimerEvent& event) +{ + try{ + if (imu_data_received_ && localisation_data_received_ && graphml_file_path_is_set_) { + + imu_data_received_ = false; + localisation_data_received_ = false; + std::cout << "\n\nmpc_started_: " << mpc_started_ << std::endl; + if (!mpc_started_) { + + ROS_INFO("scenerio_name_: %s", scenerio_name_.c_str()); + MpcStartSetting mpc_start_setting(scenerio_name_, *this); + + mpc_started_ = true; + ROS_INFO("MPC started"); + // args["p"] = DM::vertcat(mpc_start_setting., state_target); + } + + // ROS_ERROR("\n\nControl Callback is called\n\n"); + MpcRunning mpc_running(*this); + // ROS_ERROR("\n\nControl Callback is finished\n\n"); + + // args["x0"] = vertcat( + // reshape(X0.T(), n_states * (N + 1), 1), + // reshape(u0.T(), n_controls * N, 1) + // ); + + // // Uncomment for debugging + // // std::cout << "args['x0']: " << args["x0"] << std::endl; + // // std::cout << "args['p']: " << args["p"] << std::endl; + // // std::cout << "u0: " << u0.T() << std::endl; + // // std::cout << "X0: " << X0.T() << std::endl; + + // std::map sol = solver( + // {{"x0", args["x0"]}, + // {"lbx", args["lbx"]}, + // {"ubx", args["ubx"]}, + // {"lbg", args["lbg"]}, + // {"ubg", args["ubg"]}, + // {"p", args["p"]}} + // ); + + // u = reshape((sol["x"](Slice(n_states * (N + 1), sol["x"].size1()))).T(), n_controls, N).T(); + + } + } + catch (const std::exception& e) { + ROS_FATAL("Exception caught during processing and publishing data: %s", e.what()); + return; + } +} + +void MpcNode::get_parameters() +{ + try { + std::cout << "\nGetting parameters" << std::endl; + + nh_.param("control_rate", control_rate_, 15); + nh_.param("graph_file_path", graphml_filename_, std::string("fixed2.graphml")); + nh_.param("scenerio_name", scenerio_name_, std::string("initial_settings")); + nh_.param("Q_x", initial_settings_.Q_x, 1.0); + nh_.param("Q_y", initial_settings_.Q_y, 2.0); + nh_.param("Q_theta", initial_settings_.Q_theta, 0.1); + nh_.param("R1", initial_settings_.R1, 0.5); + nh_.param("R2", initial_settings_.R2, 0.05); + nh_.param("step_horizon", initial_settings_.step_horizon, 0.3); + nh_.param("N", initial_settings_.N, 1); + nh_.param("rob_diam", initial_settings_.rob_diam, 0.354); + nh_.param("wheel_radius", initial_settings_.wheel_radius, 1.0); + nh_.param("L_value", initial_settings_.L_value, 0.1); + nh_.param("Ly", initial_settings_.Ly, 0.03); + nh_.param("v_max", initial_settings_.v_max, 0.25); + nh_.param("v_min", initial_settings_.v_min, -0.15); + nh_.param("omega_max", initial_settings_.omega_max, M_PI/10); //degree + nh_.param("omega_min", initial_settings_.omega_min, -M_PI/10); //degree + + if (!graphml_file_path_is_set_) { + try { + + graphml_file_path_ = find_file(graphml_filename_); + graphml_file_path_is_set_ = true; + ROS_INFO("GraphML file path: %s", graphml_file_path_.c_str()); + processAndPublishPath processAndPublishPath(graphml_file_path_,initial_settings_.source_node,initial_settings_.target_node, *this); + // Debug the pathGoalsYawDegreeCopy_ + if (!djikstra_outputs_.pathGoalsYawDegree.empty()) { + std::cout << "Path Goals Yaw Degree Copy:" << std::endl; + for (const auto& [id, x, y, angle] : djikstra_outputs_.pathGoalsYawDegree) { + std::cout << "Node ID: " << id << ", X: " << x << ", Y: " << y << ", Angle: " << angle << std::endl; + } + } else { + std::cout << "Path Goals Yaw Degree Copy is empty!" << std::endl; + } + } catch (const std::runtime_error& e) { + ROS_FATAL("%s", e.what()); + ros::shutdown(); + return; + } + } + + + } catch (const std::exception& e) { + ROS_FATAL("Exception caught during processing and publishing data: %s", e.what()); + return; + } +} + + +// void MpcNode::imageCb(const sensor_msgs::Image::ConstPtr& msg) +// { +// try { +// cv_bridge::CvImagePtr cv_ptr; +// cv_ptr = cv_bridge::toCvCopy(msg, sensor_msgs::image_encodings::BGR8); + +// } catch (cv_bridge::Exception& e) { +// ROS_ERROR("cv_bridge exception: %s", e.what()); +// return; +// } +// } + +void MpcNode::obstacleDetectionCb(const obstacle_detector::Obstacles::ConstPtr& msg) +{ + try { + center_x_.clear(); + center_y_.clear(); + if (msg->circles.empty()) { + ROS_INFO_THROTTLE(1, "\nNo obstacles detected"); + return; + } + else { + ROS_INFO_THROTTLE(1, "\nObstacle data received"); + for (const auto& obs : msg->circles) { + center_x_.push_back(obs.center.x); + center_y_.push_back(obs.center.y); + std::cout << "Obstacle center: " << obs.center.x << ", " << obs.center.y << std::endl; + } + } + + } catch (const std::exception& e) { + ROS_FATAL("Exception caught during processing and publishing data: %s", e.what()); + return; + } +} + +void MpcNode::imuCb(const sensor_msgs::Imu::ConstPtr& msg) +{ + + try{ + ROS_INFO_ONCE("\n\nIMU data received", msg->orientation.x, msg->orientation.y, msg->orientation.z, msg->orientation.w); + tf2::Quaternion quat(msg->orientation.x, msg->orientation.y, msg->orientation.z, msg->orientation.w); + + double roll, pitch, yaw; + tf2::Matrix3x3(quat).getRPY(localisation_data_.roll, localisation_data_.pitch, localisation_data_.yaw); + imu_data_received_ = true; + } catch (const std::exception& e) { + imu_data_received_ = false; + ROS_FATAL("Exception caught during processing and publishing data: %s", e.what()); + return; + } + +} + +void MpcNode::localisationCb(const geometry_msgs::PoseWithCovarianceStamped::ConstPtr& msg) +{ + try { + ROS_INFO_ONCE("\n\nLocalisation data received: X = %f, Y = %f", msg->pose.pose.position.x, msg->pose.pose.position.y); + localisation_data_.x = msg->pose.pose.position.x; + localisation_data_.y = msg->pose.pose.position.y; + localisation_data_received_ = true; + } catch (const std::exception& e) { + localisation_data_received_ = false; + ROS_FATAL("Exception caught during processing and publishing data: %s", e.what()); + return; + } +} + diff --git a/src/example/scripts/mpc_node.cpp b/src/example/scripts/mpc_node.cpp new file mode 100644 index 0000000..fd574bc --- /dev/null +++ b/src/example/scripts/mpc_node.cpp @@ -0,0 +1,53 @@ +/* +* MIT License +* +* Copyright (c) 2024 Mehmet Baha Dursun +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +/* + * Author: Mehmet Baha Dursun + */ + +#include "mekatronom/MpcNode.hpp" +#include +int main(int argc, char** argv) { + ros::init(argc, argv, "MpcNode", ros::init_options::NoRosout); + ros::NodeHandle nh(""); + ros::NodeHandle nh_local("~"); + + try { + ROS_INFO("[MPC Node]: Initializing node"); + unsigned int cpu_cores = std::thread::hardware_concurrency(); + ROS_INFO_STREAM("[MPC Node]: Number of CPU cores: " << cpu_cores); + MpcNode mpcNode(nh, nh_local); + ros::AsyncSpinner spinner(0); // ROS will use a thread for each CPU core + spinner.start(); + ros::waitForShutdown(); + } + catch (const char* s) { + ROS_FATAL_STREAM("[MPC Node]: " << s); + } + catch (...) { + ROS_FATAL_STREAM("[MPC Node]: Unexpected error"); + } + + return 0; +} \ No newline at end of file diff --git a/src/example/src/camera.py b/src/example/src/camera.py index 4a34f14..cfcf8f3 100755 --- a/src/example/src/camera.py +++ b/src/example/src/camera.py @@ -34,31 +34,739 @@ import numpy as np from sensor_msgs.msg import Image from cv_bridge import CvBridge, CvBridgeError +import math +from geometry_msgs.msg import Twist +from std_msgs.msg import String +from RcBrainThread import RcBrainThread +import json -class CameraHandler(): +class lanetracking(): # ===================================== INIT========================================== - def __init__(self): + def __init__(self,twist_topic_name,camera_topic_name): """ Creates a bridge for converting the image from Gazebo image intro OpenCv image """ + + #bölgelere ilk değeri 0 veriyorum. baslangic + self.on = 0 + self.onsag = 0 + self.onsol = 0 + self.sag = 0 + self.sol = 0 + self.onsolengel = 0 + self.onsagengel = 0 + self.speed = 0.2 + self.sure = 0 + self.derece_verisi_counter = 0 + self.engel = 0 + self.orta_cizgi_yakinlik = 0 + self.serit_degisti = 0 + self.carside_cb = True # Normalde false simdilik True + self.serit_kontrol = 0 + self.konum = 0 + self.arac_eylem = "yol_temiz" + self.yol_kapali = 0 + self.lane_type = "sheesh" + self.eylem = "yol_temiz" + + self.degree_veri = 0 + #self.ser =serial.Serial(baudrate=115200,port='/dev/ttyACM0') + #bölge ayarlaması bitti + + #arac boyut tanımlamaları: + self.vehicleWidth = 0.6 + self.vehicleLength = 0.28 + + # isik tanımlamaları + self.kirmizi_isik = 0 + self.yesil_isik = 0 + self.durak_giriliyor = 0 + + self.iterasyon_degeri=0 + self.yaw_degree = 0 + self.mpc_iter = 0 + + # + self.y_offset = 0 + self.x_offset = 0 + self.depth_x = 0 + self.depth_y = 0 + self.depth_cb_done = False + self.steering_angle = 0 + self.mavi_cizgi_sol_üst = 0 + + self.theta = 0 + + self.not_shifted_array = [] + self.shifted_array = [] + self.shifted_array_aviable = False + self.odometry_cb = False + self.hedef_x = 0 + + self.twist = Twist() # Gazeboda arabayi sürmek için + self.msg = String() + self.bridge = CvBridge() self.cv_image = np.zeros((640, 480)) - rospy.init_node('CAMnod', anonymous=True) - self.image_sub = rospy.Subscriber("/automobile/image_raw", Image, self.callback) - rospy.spin() + #rospy.init_node('CAMnod', anonymous=True) + self.image_sub = rospy.Subscriber(camera_topic_name, Image, self.image_callback) - def callback(self, data): - """ - :param data: sensor_msg array containing the image in the Gazsbo format - :return: nothing but sets [cv_image] to the usefull image that can be use in opencv (numpy array) - """ - self.cv_image = self.bridge.imgmsg_to_cv2(data, "bgr8") - cv2.imshow("Frame preview", self.cv_image) - key = cv2.waitKey(1) - - -if __name__ == '__main__': + self.carControl = rospy.Publisher(twist_topic_name, String, queue_size=2) + #self.twist_pub = rospy.Publisher(twist_topic_name, Twist, queue_size=10) + + + def image_callback(self, data): + print("Line Detection Prs") + + img = self.bridge.imgmsg_to_cv2(data, "bgr8") + gray_image = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) + img_width = 640 + img_height = 480 + #img = cv2.resize(img, (img_width, img_height)) + #cv2.imshow('color', img) + # cv2.waitKey(3) + # cv2.imwrite('imgContourPers.jpg', img) + + # c_thresh1 = 0 + # c_thresh2 = 70 + hue_min = 0 + hue_max = 60 + sat_min = 0 + sat_max = 255 + val_min = 70 + val_max = 255 + + # Görüntüyü HSV renk uzayına dönüştürün + # hsv_img = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) + + # HSV değerlerine dayalı maske oluşturun + lower_bound = np.array([hue_min, sat_min, val_min]) + upper_bound = np.array([hue_max, sat_max, val_max]) + # mask = cv2.inRange(hsv_img, lower_bound, upper_bound) + + imgBlur= cv2.GaussianBlur(img, (7, 7), 1) + + imgHSV= cv2.cvtColor(imgBlur, cv2.COLOR_BGR2HSV) + + imgMask = cv2.inRange(gray_image, 200, 255) + #cv2.imshow('mask', imgMask) + + kernel = np.ones((5, 5)) + imgDil = cv2.dilate(imgMask, kernel, iterations=2) + + imgContour = imgDil.copy() + + contours, hierarchy = cv2.findContours(imgDil, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE) + + cv2.drawContours(imgContour, contours, -1, (255, 0, 255), 3) + + # cv2.imshow("imgcounterLeft", imgContourLeft) + # cv2.imshow("imgcounterRight", imgContourRight) + + # for cntLeft in contoursLeft: + # areaLeft = cv2.contourArea(cntLeft) + # if areaLeft > 1000: # DÜZENLENEBİLİR - Burada sadece alanı 1000 birim + # periLeft = cv2.arcLength(cntLeft, True) + # approxLeft = cv2.approxPolyDP(cntLeft, 0.02 * periLeft, True) + # xLeft, yLeft, wLeft, hLeft = cv2.boundingRect(approxLeft) + # aLeft = int(wLeft / 2 + xLeft) + # bLeft = int(hLeft / 2 + yLeft) + # cv2.rectangle(imgContourLeft, (xLeft, yLeft), (xLeft + wLeft, yLeft + hLeft), (0, 255, 0), 5) + # cv2.rectangle(imgContourLeft, (aLeft, yLeft), (aLeft, yLeft + hLeft), (255, 0, 0), 2) + # cv2.line(imgContourLeft, (aLeft, bLeft), (640, 27), (255, 255, 0), 1) + # # distanceLeft = int(640 - aLeft) + # # solSeritDurum = "OK" + # # else: solSeritDurum = "KAYIP" + + width, height = 640, 480 + + bottom_right = [990, 480] # 640, 480 + bottom_left = [-350, 480] # 0, 480 + top_right = [600, 240] + top_left = [40, 240] + + src = np.float32([top_left, top_right, bottom_left, bottom_right]) + + # Dönüşüm sonucu alınacak köşe noktalarını belirleme + dst = np.float32([[0, 0], [width, 0], [0, height], [width, height]]) + matrix = cv2.getPerspectiveTransform(src, dst) + imgWarp = cv2.warpPerspective(img, matrix, (width, height)) + + imgContourPers = imgWarp.copy() + gray_image = cv2.cvtColor(imgContourPers, cv2.COLOR_BGR2GRAY) + imgMaskGray = cv2.inRange(gray_image, 200, 255) + imgHSVPers = cv2.cvtColor(imgWarp, cv2.COLOR_BGR2HSV) + imgMaskPers = cv2.inRange(imgHSVPers, lower_bound, upper_bound) + + imgDilPers = cv2.dilate(imgMaskGray, kernel, iterations=2) + contoursPers, hierarchyPers = cv2.findContours(imgDilPers, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE) + cv2.drawContours(imgContourPers, contoursPers, -1, (255, 0, 255), 1) + + + # 640 x 360 için + pixel_val = np.arange(25, (img_height - 25), 25) + + # 1280 x 720 için + # pixel_val = np.arange(25, (img_height - 25), 25) + def calculate_steering_angle_new(L, R, T): + #L arac uzunlugu + #R curve yaricapi + #T tekerler arasi mesafe + + steering_angle_in = math.atan(L/(R-(0.5*T))) + steering_angle_out = math.atan(L/(R+(0.5*T))) + + print(f"Steering angle in {steering_angle_in}") + print(f"Steering angle out {steering_angle_out}\n") + + return steering_angle_in, steering_angle_out + + def calculate_steering_angle_new_oldstyle(lx, rx, ly, ry): + mid_y_left = sum(ly)/len(ly) + mid_x_left = sum(lx)/len(lx) + + mid_y_right = sum(ry) / len(ry) + mid_x_right = sum(rx) / len(rx) + + mid_steering_y = (mid_y_left + mid_y_right) / 2 + mid_steering_x = (mid_x_left + mid_x_right) / 2 + steering_point = [mid_steering_x, mid_steering_y] + print(f"Steering Point {steering_point}") + bottom_mid = [320, 480] + # cv2.line(merged_img, steering_point, bottom_mid, color=(0, 255, 0), thickness=2) + steering_angle = math.atan((steering_point[0] - bottom_mid[0]) / (steering_point[1] - bottom_mid[1])) + steering_angle = 90 - (90 - math.degrees(steering_angle)) + self.steering_angle = steering_angle + green_text = "\033[92m" # green + reset_text = "\033[0m" # reset to default color + print(f"{green_text}Steering Angle {self.steering_angle}{reset_text}") + # 180 - (90 - math.degrees(steering_angle)) + + steering_angle_limit = 20 + if self.steering_angle > steering_angle_limit: + self.steering_angle = steering_angle_limit + elif self.steering_angle < -steering_angle_limit: + self.steering_angle = -steering_angle_limit + + print(f"Steering angle old style {self.steering_angle}") + + return steering_point + + def get_poly_points(left_fit, right_fit): + ''' + Get the points for the left lane/ right lane defined by the polynomial coeff's 'left_fit' + and 'right_fit' + :param left_fit (ndarray): Coefficients for the polynomial that defines the left lane line + :param right_fit (ndarray): Coefficients for the polynomial that defines the right lane line + : return (Tuple(ndarray, ndarray, ndarray, ndarray)): x-y coordinates for the left and right lane lines + ''' + ysize, xsize = 480, 640 + + # Get the points for the entire height of the image + plot_y = np.linspace(0, ysize - 1, ysize) + plot_xleft = left_fit[0] * plot_y ** 2 + left_fit[1] * plot_y + left_fit[2] + plot_xright = right_fit[0] * plot_y ** 2 + right_fit[1] * plot_y + right_fit[2] + + # But keep only those points that lie within the image + plot_xleft = plot_xleft[(plot_xleft >= 0) & (plot_xleft <= xsize - 1)] + plot_xright = plot_xright[(plot_xright >= 0) & (plot_xright <= xsize - 1)] + plot_yleft = np.linspace(ysize - len(plot_xleft), ysize - 1, len(plot_xleft)) + plot_yright = np.linspace(ysize - len(plot_xright), ysize - 1, len(plot_xright)) + + return plot_xleft.astype(int), plot_yleft.astype(int), plot_xright.astype(int), plot_yright.astype( + int) + + def compute_offset_from_center(poly_param, x_mppx): + ''' + Computes the offset of the car from the center of the detected lane lines + :param poly_param (ndarray): Set of 2nd order polynomial coefficients that represent the detected lane lines + :param x_mppx (float32): metres/pixel in the x-direction + :return (float32): Offset + ''' + plot_xleft, plot_yleft, plot_xright, plot_yright = get_poly_points(poly_param[0], poly_param[1]) + + lane_center = (plot_xright[-1] + plot_xleft[-1]) / 2 + car_center = 640 / 2 + + offset = (lane_center - car_center) * x_mppx + return offset + + def compute_curvature(poly_param, y_mppx, x_mppx): + ''' + Computes the curvature of the lane lines (in metres) + :param poly_param (ndarray): Set of 2nd order polynomial coefficients that represent the detected lane lines + :param y_mppx (float32): metres/pixel in the y-direction + :param x_mppx (float32): metres/pixel in the x-direction + :return (float32): Curvature (in metres) + ''' + plot_xleft, plot_yleft, plot_xright, plot_yright = get_poly_points(poly_param[0], poly_param[1]) + + y_eval = np.max(plot_yleft) + + left_fit_cr = np.polyfit(plot_yleft * y_mppx, plot_xleft * x_mppx, 2) + right_fit_cr = np.polyfit(plot_yright * y_mppx, plot_xright * x_mppx, 2) + + left_curverad = ((1 + (2 * left_fit_cr[0] * y_eval * y_mppx + left_fit_cr[1]) ** 2) ** 1.5) / np.absolute( + 2 * left_fit_cr[0]) + right_curverad = ((1 + ( + 2 * right_fit_cr[0] * y_eval * y_mppx + right_fit_cr[1]) ** 2) ** 1.5) / np.absolute( + 2 * right_fit_cr[0]) + + return left_curverad, right_curverad + + + def fit_polynom(mask, lx, rx, ly, ry): + # Sol ve sağ noktaları al + points_left = [(lx[i], ly[i]) for i in range(min(len(lx), len(ly)))] + points_right = [(rx[i], ry[i]) for i in range(min(len(rx), len(ry)))] + + # Sol taraf için polinom uydur + left_x = np.array([point[0] for point in points_left]) + left_y = np.array([point[1] for point in points_left]) + left_poly = np.polyfit(left_y, left_x, 2) + + # Sağ taraf için polinom uydur + right_x = np.array([point[0] for point in points_right]) + right_y = np.array([point[1] for point in points_right]) + right_poly = np.polyfit(right_y, right_x, 2) + + # Türev hesapla + left_turev = np.polyder(left_poly) + right_turev = np.polyder(right_poly) + + x_point = 200 # Örneğin x = 2 noktasındaki eğimi bulalım + slope_at_x_left = np.polyval(left_turev, x_point) + slope_at_x_right = np.polyval(right_turev, x_point) + + print("Yolun Eğimi\n") + print(f"sol_türev {left_turev}") + print(f"sag_türev {right_turev}\n") + print(f"x = {x_point} noktasında eğim sol {slope_at_x_left}") + print(f"x = {x_point} noktasında eğim sag {slope_at_x_right}\n") + + left_curverad, right_curverad = compute_curvature([left_poly, right_poly], y_mppx=0.002, x_mppx=0.002) + print(f"Left Curve {left_curverad}") + print(f"Right Curve {right_curverad}") + + + # Mask üzerine eğrileri çiz + for y in range(mask.shape[0]): + left_x = int(np.polyval(left_poly, y)) + right_x = int(np.polyval(right_poly, y)) + cv2.circle(mask, (left_x, y), 2, (255, 0, 0), -1) # Sol şerit + cv2.circle(mask, (right_x, y), 2, (0, 0, 255), -1) # Sağ şerit + + # # Sol köşenin eğimini göster + # if 0 < y < mask.shape[0] - 1: + # left_gradient = left_turev[y] + # if abs(left_gradient) > 0.1: # Eğim eşik değeri + # cv2.putText(mask, f'{left_gradient:.2f}', (left_x, y), cv2.FONT_HERSHEY_SIMPLEX, 0.5, + # (0, 255, 0), 1, cv2.LINE_AA) + # + # # Sağ köşenin eğimini göster + # right_gradient = right_turev[y] + # if abs(right_gradient) > 0.1: # Eğim eşik değeri + # cv2.putText(mask, f'{right_gradient:.2f}', (right_x, y), cv2.FONT_HERSHEY_SIMPLEX, 0.5, + # (0, 255, 0), 1, cv2.LINE_AA) + + + return mask, left_poly, right_poly, left_curverad, right_curverad + + def draw(mask, poly_param, curve_rad, offset): + ''' + Utility function to draw the lane boundaries and numerical estimation of lane curvature and vehicle position. + :param mask (ndarray): Original image + :param poly_param (ndarray): Set of 2nd order polynomial coefficients that represent the detected lane lines + :param curve_rad (float32): Lane line curvature + :param offset (float32): Car offset + :return (ndarray): Image with visual display + ''' + + left_fit = poly_param[0] + right_fit = poly_param[1] + plot_xleft, plot_yleft, plot_xright, plot_yright = get_poly_points(left_fit, right_fit) + + pts_left = np.array([np.transpose(np.vstack([plot_xleft, plot_yleft]))]) + pts_right = np.array([np.flipud(np.transpose(np.vstack([plot_xright, plot_yright])))]) + + # Write data on the image + if (left_fit[1] + right_fit[1]) / 2 > 0.05: + text = 'Left turn, curve radius: {:04.2f} m'.format(curve_rad) + elif (left_fit[1] + right_fit[1]) / 2 < -0.05: + text = 'Right turn, curve radius: {:04.2f} m'.format(curve_rad) + else: + text = 'Straight' + + cv2.putText(mask, text, (50, 60), cv2.FONT_HERSHEY_DUPLEX, 1.2, (255, 255, 255), 2, cv2.LINE_AA) + + direction = '' + if offset > 0: + direction = 'left' + elif offset < 0: + direction = 'right' + + text = '{:0.1f} cm {} of center'.format(abs(offset) * 100, direction) + cv2.putText(mask, text, (50, 110), cv2.FONT_HERSHEY_DUPLEX, 1.2, (255, 255, 255), 2, cv2.LINE_AA) + + return mask + + + def sliding_windows(mask, image_org): + # Histogram + histogram = np.sum(mask[mask.shape[0] // 2:, :], axis=0) + midpoint = int(histogram.shape[0] / 2) + left_base = np.argmax(histogram[:midpoint]) + right_base = np.argmax(histogram[midpoint:]) + midpoint + + # Sliding Window + y = 472 + lx = [] + rx = [] + ly = [] + ry = [] + + msk = mask.copy() + + while y > 0: + ## Left threshold + img = mask[y - 40:y, left_base - 50:left_base + 50] + contours, _ = cv2.findContours(img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) + for contour in contours: + M = cv2.moments(contour) + if M["m00"] != 0: + cx = int(M["m10"] / M["m00"]) + cy = int(M["m01"] / M["m00"]) + lx.append(left_base - 50 + cx) + ly.append(y) + left_base = left_base - 50 + cx + + ## Right threshold + img = mask[y - 40:y, right_base - 50:right_base + 50] + contours, _ = cv2.findContours(img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) + for contour in contours: + M = cv2.moments(contour) + if M["m00"] != 0: + cx = int(M["m10"] / M["m00"]) + cy = int(M["m01"] / M["m00"]) + rx.append(right_base - 50 + cx) + ry.append(y) + right_base = right_base - 50 + cx + + cv2.rectangle(image_org, (left_base - 50, y), (left_base + 50, y - 40), (0, 255, 255), 2) + cv2.rectangle(image_org, (right_base - 50, y), (right_base + 50, y - 40), (0, 255, 255), 2) + y -= 40 + return msk, lx, rx, ly, ry + + def calculate_steering_angle(con_right, + con_left): # Sag Taraf Conturlerini ve sol taraf conturlerini alarak steering angle hesaplar + steering_line_threshold = 250 # Yeşil nokta sınır + print(f"Con Right {con_right}\n Con Left {con_left}") + filt_con_right = {key: value for key, value in con_right.items() if + key > steering_line_threshold} # Piksel değeri y'de 250 (260-270-280 gibi) altında olanları almak + filt_con_left = {key: value for key, value in con_left.items() if key > steering_line_threshold} + + min_x_right = {} + max_x_left = {} + + for key in filt_con_right.keys(): + if len(filt_con_right[key]) > 0: + min_x_right[key] = min(filt_con_right[key]) + + # filt_min_x_right = {key: value for key, value in min_x_right.items() if value < img_width/2 + x_axis_offset} # X ekseninde offset ile tam ortaya steering line çekilmesini engelliyorum + + for key in filt_con_left.keys(): + if len(filt_con_left[key]) > 0: + max_x_left[key] = max(filt_con_left[key]) + + # filt_max_x_left = {key: value for key, value in max_x_left.items() if value > img_width/2 - x_axis_offset} + + print(f"Min X Right {min_x_right}\n Max X Left {max_x_left}") + + no_lines_left = False + no_lines_right = False + + if len(list(max_x_left.keys())) > 0: + sol_taraf_max_y = min(list(max_x_left.keys())) + sol_taraf_max_x = max_x_left[sol_taraf_max_y] + else: + print("Sol Taraf Max X Yok") + no_lines_left = True + sol_taraf_max_y = 300 + sol_taraf_max_x = 0 + if len(list(min_x_right.keys())) > 0: + sag_taraf_max_y = min(list(min_x_right.keys())) + left_offset = img_width / 2 + sag_taraf_max_x = min_x_right[sag_taraf_max_y] + left_offset + else: + print("Sağ Taraf Max X Yok") + no_lines_right = True + sag_taraf_max_y = 300 + sag_taraf_max_x = 640 + + if sol_taraf_max_y > sag_taraf_max_y or no_lines_left: + if no_lines_left: + steering_point = [0, steering_line_threshold] + else: + steering_point = [int((sol_taraf_max_x + sag_taraf_max_x) / 2), + int((sol_taraf_max_y + sag_taraf_max_y) / 2)] + print(f"Sola dön - Sol taraf max_y = {sol_taraf_max_y} - Sağ Taraf max_y = {sag_taraf_max_y}") + print(f"Sola dön - Sol taraf max_x = {sol_taraf_max_x} - Sağ Taraf max_x = {sag_taraf_max_x}") + elif sol_taraf_max_y < sag_taraf_max_y or no_lines_right: + if no_lines_right: + steering_point = [640, steering_line_threshold] + else: + steering_point = [int((sol_taraf_max_x + sag_taraf_max_x) / 2), + int((sol_taraf_max_y + sag_taraf_max_y) / 2)] + print(f"Sağa dön - Sol taraf max_y = {sol_taraf_max_y} - Sağ Taraf max_y = {sag_taraf_max_y}") + print(f"Sağa dön - Sol taraf max_x = {sol_taraf_max_x} - Sağ Taraf max_x = {sag_taraf_max_x}") + else: + print(f"Düz git - Sol taraf max_y = {sol_taraf_max_y} - Sağ Taraf max_y = {sag_taraf_max_y}") + print(f"Düz git - Sol taraf max_x = {sol_taraf_max_x} - Sağ Taraf max_x = {sag_taraf_max_x}") + steering_point = [int((sol_taraf_max_x + sag_taraf_max_x) / 2), + int((sol_taraf_max_y + sag_taraf_max_y) / 2)] + + print(f"Steering Point {steering_point}") + bottom_mid = [320, 480] + #cv2.line(merged_img, steering_point, bottom_mid, color=(0, 255, 0), thickness=2) + steering_angle = math.atan((steering_point[0] - bottom_mid[0]) / (steering_point[1] - bottom_mid[1])) + steering_angle = 90 - (90 - math.degrees(steering_angle)) + self.steering_angle = steering_angle + green_text = "\033[92m" # green + reset_text = "\033[0m" # reset to default color + print(f"{green_text}Steering Angle {self.steering_angle}{reset_text}") + # 180 - (90 - math.degrees(steering_angle)) + + steering_angle_limit = 20 + if self.steering_angle > steering_angle_limit: + self.steering_angle = steering_angle_limit + elif self.steering_angle < -steering_angle_limit: + self.steering_angle = -steering_angle_limit + + steering_angle_error_th = 3 + for prev_steer_angle in self.collective_steering_angle: + if abs(prev_steer_angle - self.steering_angle) < steering_angle_error_th: + steering_angle = self.collective_steering_angle[-1] + + # 30 kere aynı değer geldiyse dön artık yeter be adam + for prev_steer_angle in self.collective_steering_angle: + if prev_steer_angle != steering_angle: + break + + if len(self.collective_steering_angle) < 30: + self.collective_steering_angle.append(self.steering_angle) + else: + collective_steering_angle = [0] + + # self.twist.angular.z = float(math.degrees(self.steering_angle)) + # self.twist.linear.x = float(0.2) + + def draw_mean_circle(grup_x_given, img): + points_green = [] + for key in grup_x_given.keys(): + if grup_x_given[key] == []: + continue + # print(grup_x_given[key]) + x1x2 = grup_x_given[key] + # print(f"x1x2 Tipi = {type(x1x2)}") + x_mean = int((x1x2[0] + x1x2[1]) / 2) + cv2.circle(img, (x_mean, key), 1, (0, 255, 0), thickness=2) + points_green.append((x_mean, key)) + return points_green + + def draw_lines_with_pixels(points, img): + # En az iki nokta gereklidir + if len(points) < 2: + print("En az iki nokta gereklidir.") + return [], img + + line_pixels = [] # Tüm çizgi pikselleri burada saklanacak + + # İlk iki noktayı al + p1 = points[0] + p2 = points[1] + + # İlk iki noktadan bir çizgi çiz + cv2.line(img, p1, p2, (255, 0, 0), thickness=2) + + # İlk iki nokta arasındaki pikselleri ekle + line_pixels.extend(get_line_pixels(p1, p2)) + + # Kalan noktaları işleme + for i in range(2, len(points)): + # Geçerli noktayı al + p = points[i] + + # Son noktadan çizgi çek + cv2.line(img, p2, p, (255, 0, 0), thickness=2) + + # Çizginin piksellerini ekle + line_pixels.extend(get_line_pixels(p2, p)) + + # Sonraki çizgi için p2'yi güncelle + p2 = p + + # İşlenmiş görüntüyü ve çizgi piksellerini döndür + return line_pixels + + def get_line_pixels(p1, p2): + line_pixels = [] # Çizginin piksellerini burada saklanacak + x1, y1 = p1 + x2, y2 = p2 + dx = abs(x2 - x1) + dy = abs(y2 - y1) + x, y = x1, y1 + sx = -1 if x1 > x2 else 1 + sy = -1 if y1 > y2 else 1 + + if dx > dy: + err = dx / 2.0 + while x != x2: + line_pixels.append((x, y)) + err -= dy + if err < 0: + y += sy + err += dx + x += sx + else: + err = dy / 2.0 + while y != y2: + line_pixels.append((x, y)) + err -= dx + if err < 0: + x += sx + err += dy + y += sy + line_pixels.append((x, y)) + return line_pixels + + img_sliding_window, lx, rx, ly, ry = sliding_windows(imgDilPers, imgWarp) + mask_with_curve, left_poly, right_poly, left_curverad, right_curverad = fit_polynom(imgWarp, lx=lx, rx=rx, ly=ly, ry=ry) + curvature = (left_curverad + right_curverad) / 2 + offset_calc = compute_offset_from_center([left_poly, right_poly], x_mppx=0.002) + draw(mask=mask_with_curve, poly_param=[left_poly, right_poly], curve_rad=curvature, offset=offset_calc) + # steering_angle_in, steering_angle_out = calculate_steering_angle_new(L=0.6, R=curvature, T=0.2) + steering_point = calculate_steering_angle_new_oldstyle(lx, rx, ly, ry) + + print(f"lx :\n {lx}") + print(f"rx :\n {rx}") + print(f"ly :\n {ly}") + print(f"ry :\n {ry}") + + # cv2.circle(img, bottom_left, 5, (0, 0, 255), -1) + # cv2.circle(img, bottom_right, 5, (0, 0, 255), -1) + # cv2.circle(img, top_left, 5, (0, 0, 255), -1) + # cv2.circle(img, top_right, 5, (0, 0, 255), -1) + + self.steerLateral = 0.3 + self.carControlPublisher() + + cv2.imshow('Contour', imgContourPers) + cv2.imshow("img-given", img) + cv2.imshow('Perspective', mask_with_curve) + cv2.imshow("SlidingWindows", img_sliding_window) + + + cv2.waitKey(3) + # image_message = self.bridge.cv2_to_imgmsg(img, "bgr8") + + # print(len(image_message.data)) + + # cv2.imshow("IMG Wrap Right", imgWarpRight) + # cv2.imshow("IMG Wrap Left", imgWarpLeft) + + # world_coords = calculate_real_world_coor(matrix, contoursPers) + # + # print("World Coordinates: ", world_coords) + # print("World Coordinates Shape: ", world_coords.shape) + # + # # İşaretlenmiş dünya koordinatlarını normal görüntüde işaretleme + # img_world_coor = img.copy() + # cv2.circle(img_world_coor, (int(world_coords[0]), int(world_coords[1])), 5, (0, 255, 0), -1) + # + # cv2.imshow("imgWorldCoords", img_world_coor) + + + def carControlPublisher(self): + carData = {} + carData = { + 'action': '2', + 'steerAngle': self.steering_angle + # 'steerAngle': 30.0 + } + # print("data",carData) + # print("math.degrees(float(self.DM2Arr(self.u[0, 1]))) ",math.degrees(float(self.DM2Arr(self.u[0, 1]))) ) + self.carData = json.dumps(carData) + self.carControl.publish(self.carData) + #rospy.sleep(0.01) + car2Data = {} + # İkinci mesaj için veriler + car2Data = { + 'action': '1', + 'speed': self.steerLateral + #'speed': 0.0 + } + #print("data",car2Data) + self.car2Data = json.dumps(car2Data) + self.carControl.publish(self.car2Data) + + +# if __name__ == '__main__': + +# rospy.init_node("lanetracking") +# lane_tracker = lanetracking(twist_topic_name='/automobile/command', +# camera_topic_name ='/automobile/image_raw') +# try: +# rospy.spin() +# except KeyboardInterrupt or IndexError: +# data = {} +# data = { +# 'action': '2', +# 'steerAngle': float(0.0) +# } +# print("data",data) +# data = json.dumps(data) +# lane_tracker.publish(data) +# rospy.sleep(0.1) + +# # İkinci mesaj için veriler +# data = { +# 'action': '1', +# 'speed': float(0.0) +# } +# data = json.dumps(data) +# lane_tracker.publish(data) +# lane_tracker.destroy_node() +# rospy.shutdown() + +def main(): + rospy.init_node('lane_tracker', anonymous=True) + lane_tracker = lanetracking(twist_topic_name='/automobile/command', + camera_topic_name ='/automobile/image_raw') + + rate = rospy.Rate(20) # 10Hz try: - nod = CameraHandler() - except rospy.ROSInterruptException: - pass \ No newline at end of file + while not rospy.is_shutdown(): + # Sürekli çalışacak kodlar burada + rate.sleep() + except KeyboardInterrupt: + data = {} + data = { + 'action': '2', + 'steerAngle': float(0.0) + } + print("data",data) + data = json.dumps(data) + lane_tracker.publish(data) + rospy.sleep(0.1) + + # İkinci mesaj için veriler + data = { + 'action': '1', + 'speed': float(0.0) + } + data = json.dumps(data) + lane_tracker.publish(data) + cv2.destroyAllWindows() + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/src/example/src/control.py b/src/example/src/control.py index 72f8021..0eb6d9f 100755 --- a/src/example/src/control.py +++ b/src/example/src/control.py @@ -30,93 +30,123 @@ import json from pynput import keyboard - -from RcBrainThread import RcBrainThread from std_msgs.msg import String - +from sensor_msgs.msg import Image +import cv2 import rospy +from cv_bridge import CvBridge, CvBridgeError + -class RemoteControlTransmitterProcess(): +class CarControl(): # ===================================== INIT========================================== def __init__(self): """Run on the PC. It forwards the commans from the user via KeboardListenerThread to the RcBrainThread. The RcBrainThread converts them into actual commands and sends them to the remote via a socket connection. """ + self.bridge = CvBridge() + self.dirKeys = ['w', 'a', 's', 'd'] self.paramKeys = ['t','g','y','h','u','j','i','k', 'r', 'p'] self.pidKeys = ['z','x','v','b','n','m'] self.allKeys = self.dirKeys + self.paramKeys + self.pidKeys - self.rcBrain = RcBrainThread() - + rospy.init_node('EXAMPLEnode', anonymous=False) - self.publisher = rospy.Publisher('/automobile/command', String, queue_size=1) + #self.carControl = rospy.Publisher(steeringAngle, String, queue_size=1) + self.imageTopic = rospy.Subscriber('/automobile/camera_output', Image, self.imageTopicCallback) - # ===================================== RUN ========================================== - def run(self): - """Apply initializing methods and start the threads. - """ - with keyboard.Listener(on_press = self.keyPress, on_release = self.keyRelease) as listener: - listener.join() - - # ===================================== KEY PRESS ==================================== - def keyPress(self,key): - """Processing the key pressing - Parameters - ---------- - key : pynput.keyboard.Key - The key pressed - """ - try: - if key.char in self.allKeys: - keyMsg = 'p.' + str(key.char) - - self._send_command(keyMsg) - - except: pass + self.increment = 0 + #self.twist_pub_timer = rospy.Timer(rospy.Duration(1), self.twist_pub_timer_callback) + + def imageTopicCallback(self, data): + cv_image = self.bridge.imgmsg_to_cv2(data, "bgr8") + cv2.imshow("Image window", cv_image) + print(cv_image.shape) + # def twist_pub_timer_callback(self,event): - # ===================================== KEY RELEASE ================================== - def keyRelease(self, key): - """Processing the key realeasing. + # # YOLO topic publish deneme + # # self.yolo.object = ["Cenk","Muhammed"] + # # self.yolo_pub.publish(self.yolo) - Parameters - ---------- - key : pynput.keyboard.Key - The key realeased. + # data = {} + # data = { + # 'action': '2', + # 'steerAngle': float(30.0) + # } + # self.data = json.dumps(data) + # self.carControl.publish(self.data) + # rospy.sleep(0.1) + + # # İkinci mesaj için veriler + # data = { + # 'action': '1', + # 'speed': float(0.3) + # } + # self.data = json.dumps(data) + # self.carControl.publish(self.data) + + # # self._send_command(self.array[self.increment]) + # # self.increment += 1 + # # if self.increment == 4: + # # self.increment = 0 + + # # ===================================== RUN ========================================== + # def run(self): + # """Apply initializing methods and start the threads. + # """ + # with keyboard.Listener(on_press = self.keyPress, on_release = self.keyRelease) as listener: + # listener.join() + + # # ===================================== KEY PRESS ==================================== + # def keyPress(self,key): + # pass - """ - if key == keyboard.Key.esc: #exit key - self.publisher.publish('{"action":"3","steerAngle":0.0}') - return False - try: - if key.char in self.allKeys: - keyMsg = 'r.'+str(key.char) - - self._send_command(keyMsg) - - except: pass + # # ===================================== KEY RELEASE ================================== + # def keyRelease(self, key): + # pass # ===================================== SEND COMMAND ================================= - def _send_command(self, key): - """Transmite the command to the remotecontrol receiver. + # def _send_command(self, key): + # """Transmite the command to the remotecontrol receiver. - Parameters - ---------- - inP : Pipe - Input pipe. - """ - command = self.rcBrain.getMessage(key) - if command is not None: + # Parameters + # ---------- + # inP : Pipe + # Input pipe. + # """ + # command = self.rcBrain.getMessage(key) + # if command is not None: - command = json.dumps(command) - self.publisher.publish(command) - + # #command = json.dumps(command) + # #self.publisher.publish(command) + + if __name__ == '__main__': try: - nod = RemoteControlTransmitterProcess() - nod.run() - except rospy.ROSInterruptException: - pass \ No newline at end of file + nod = CarControl() + rospy.spin() + except rospy.ROSInterruptException or KeyboardInterrupt: + rospy.signal_shutdown + + pass + + +# def main(): +# rospy.init_node('lane_tracker', anonymous=True) +# lane_tracker = CarControl(steeringAngle='/automobile/command', +# outputimage_topic_name = '/automobile/camera_output',) + +# rate = rospy.Rate(30) # 10Hz +# try: +# while not rospy.is_shutdown(): +# # Sürekli çalışacak kodlar burada +# rate.sleep() +# except KeyboardInterrupt: + +# cv2.destroyAllWindows() + +# if __name__ == '__main__': +# main() diff --git a/src/example/src/fixed2.graphml b/src/example/src/fixed2.graphml new file mode 100644 index 0000000..8c1620f --- /dev/null +++ b/src/example/src/fixed2.graphml @@ -0,0 +1,4068 @@ + + + + + + + + + + 9.34 + 0.54 + + + 10.04 + 0.54 + + + 10.99 + 0.54 + + + 11.71 + 0.54 + + + 12.41 + 0.54 + + + 9.34 + 1.3 + + + 10.03 + 1.3 + + + 10.73 + 1.3 + + + 11.41 + 1.3 + + + 13.11 + 1.3 + + + + + 4.17 + 6.89 + + + 4.17 + 6.52 + + + 4.76 + 5.94 + + + 5.13 + 5.94 + + + 5.71 + 6.89 + + + 5.13 + 7.48 + + + 4.76 + 7.48 + + + 4.94 + 6.71 + + + 4.94 + 6.71 + + + 4.94 + 6.71 + + + 4.79 + 6.52 + + + 1.93 + 6.89 + + + 1.93 + 6.52 + + + 2.52 + 5.93 + + + 2.88 + 5.93 + + + 3.47 + 6.52 + + + 3.47 + 6.89 + + + 2.7 + 6.70 + + + 2.7 + 6.70 + + + 2.7 + 6.70 + + + 2.7 + 6.70 + + + 1.93 + 4.02 + + + 1.93 + 3.65 + + + 2.51 + 3.07 + + + 2.88 + 3.07 + + + 3.47 + 3.65 + + + 3.47 + 4.02 + + + 2.88 + 4.61 + + + 2.51 + 4.61 + + + 2.7 + 3.84 + + + 2.7 + 3.84 + + + 2.7 + 3.84 + + + 2.7 + 3.84 + + + 4.17 + 4.02 + + + 4.17 + 3.65 + + + 4.75 + 3.06 + + + 5.12 + 3.06 + + + 5.71 + 4.02 + + + 5.12 + 4.60 + + + 4.75 + 4.60 + + + 4.94 + 3.83 + + + 4.94 + 3.83 + + + 4.94 + 3.83 + + + 4.94 + 3.83 + + + 14.71 + 4.01 + + + 14.71 + 3.64 + + + 15.29 + 3.05 + + + + + + + 16.25 + 4.01 + + + + + + + 15.66 + 4.60 + + + 15.29 + 4.60 + + + 15.48 + 3.83 + + + 15.48 + 3.83 + + + 15.48 + 3.83 + + + 15.48 + 3.83 + + + 1.94 + 0.36 + + + 1.94 + 0.73 + + + 2.51 + 1.30 + + + 2.88 + 1.30 + + + 3.45 + 0.36 + + + 3.45 + 0.73 + + + 2.69 + 0.54 + + + 2.69 + 0.54 + + + 2.69 + 0.54 + + + 0.35 + 10.57 + + + 0.72 + 10.57 + + + 1.29 + 10.00 + + + 1.29 + 9.63 + + + 0.35 + 9.06 + + + 0.72 + 9.06 + + + 0.53 + 9.81 + + + 0.53 + 9.81 + + + 0.53 + 9.81 + + + 0.35 + 7.48 + + + 0.72 + 7.48 + + + 1.29 + 6.90 + + + 1.29 + 6.54 + + + 0.35 + 5.96 + + + 0.72 + 5.96 + + + 0.53 + 6.72 + + + 0.53 + 6.72 + + + 0.53 + 6.72 + + + 0.35 + 4.59 + + + 0.72 + 4.59 + + + 1.29 + 4.02 + + + 1.29 + 3.65 + + + 0.35 + 3.08 + + + 0.72 + 3.08 + + + 0.53 + 3.84 + + + 0.53 + 3.84 + + + 0.53 + 3.84 + + + 2.88 + 1.68 + + + 2.88 + 2.06 + + + 2.88 + 2.44 + + + 2.89 + 2.82 + + + 2.51 + 2.69 + + + 2.51 + 2.31 + + + 2.52 + 1.93 + + + 2.51 + 1.55 + + + 1.56 + 0.72 + + + 1.18 + 0.76 + + + 0.87 + 0.98 + + + 0.73 + 1.34 + + + 0.73 + 1.72 + + + 0.73 + 2.10 + + + 0.73 + 2.48 + + + 0.74 + 2.86 + + + 0.35 + 2.70 + + + 0.35 + 2.32 + + + 0.34 + 1.94 + + + 0.34 + 1.56 + + + 0.39 + 1.19 + + + 0.51 + 0.83 + + + 0.77 + 0.56 + + + 1.11 + 0.39 + + + 1.48 + 0.32 + + + 3.79 + 4.02 + + + 3.85 + 3.65 + + + 1.55 + 4.02 + + + 1.67 + 3.65 + + + 2.88 + 4.99 + + + 2.88 + 5.37 + + + 2.88 + 5.75 + + + 2.52 + 5.55 + + + 2.52 + 5.17 + + + 2.52 + 4.79 + + + 0.73 + 4.97 + + + 0.74 + 5.35 + + + 0.74 + 5.73 + + + 0.35 + 5.59 + + + 0.34 + 5.21 + + + 0.35 + 4.83 + + + 1.67 + 6.53 + + + 1.55 + 6.88 + + + 3.85 + 6.52 + + + 3.79 + 6.88 + + + 5.13 + 7.86 + + + 5.11 + 8.24 + + + 5.04 + 8.61 + + + 4.9 + 8.97 + + + 4.71 + 9.30 + + + 4.46 + 9.59 + + + 4.17 + 9.84 + + + 3.84 + 10.03 + + + 3.48 + 10.14 + + + 3.1 + 10.19 + + + 2.72 + 10.19 + + + 2.35 + 10.11 + + + 1.97 + 10.03 + + + 1.59 + 10.01 + + + 1.67 + 9.61 + + + 2.05 + 9.62 + + + 2.4 + 9.75 + + + 2.77 + 9.84 + + + 3.15 + 9.83 + + + 3.52 + 9.74 + + + 3.87 + 9.59 + + + 4.17 + 9.37 + + + 4.42 + 9.08 + + + 4.64 + 8.78 + + + 4.73 + 8.41 + + + 4.76 + 8.04 + + + 4.76 + 7.66 + + + 0.72 + 7.86 + + + 0.73 + 8.24 + + + 0.73 + 8.62 + + + 0.34 + 8.68 + + + 0.34 + 8.30 + + + 0.34 + 7.92 + + + 3.83 + 0.35 + + + 4.21 + 0.35 + + + 4.59 + 0.35 + + + 4.97 + 0.35 + + + 5.34 + 0.35 + + + 5.72 + 0.35 + + + 6.1 + 0.36 + + + 6.44 + 0.54 + + + 6.65 + 0.86 + + + 6.66 + 1.24 + + + 6.63 + 1.62 + + + 6.62 + 2.00 + + + 6.63 + 2.38 + + + 6.63 + 2.76 + + + 6.62 + 3.14 + + + 6.61 + 3.52 + + + 6.71 + 4.05 + + + 6.1 + 4.02 + + + 4.76 + 2.68 + + + 4.76 + 2.31 + + + 4.76 + 1.93 + + + 4.75 + 1.55 + + + 4.75 + 1.17 + + + 4.8 + 0.67 + + + 4.35 + 0.77 + + + 3.97 + 0.75 + + + 4.8 + 0.44 + + + 5.07 + 0.93 + + + 5.14 + 1.31 + + + 5.16 + 1.69 + + + 5.15 + 2.07 + + + 5.12 + 2.45 + + + 5.14 + 2.83 + + + 5.13 + 4.98 + + + 5.13 + 5.37 + + + 5.14 + 5.75 + + + 4.75 + 5.56 + + + 4.75 + 5.18 + + + 4.73 + 4.80 + + + 7.03 + 0.89 + + + 7.41 + 0.90 + + + 7.79 + 0.91 + + + 8.17 + 0.90 + + + 8.54 + 0.90 + + + 8.92 + 0.90 + + + 9.3 + 0.90 + + + 9.68 + 0.91 + + + 10.06 + 0.91 + + + 10.44 + 0.92 + + + 10.82 + 0.92 + + + 11.2 + 0.93 + + + 11.58 + 0.93 + + + 11.95 + 0.93 + + + 12.33 + 0.93 + + + 12.71 + 0.93 + + + 13.09 + 0.92 + + + 13.47 + 0.92 + + + 13.85 + 0.92 + + + 14.23 + 0.92 + + + + + 14.61 + 0.93 + + + + + 14.99 + 0.94 + + + 15.36 + 0.94 + + + 15.74 + 0.94 + + + 16.12 + 0.94 + + + 16.5 + 0.92 + + + 16.84 + 1.10 + + + 17.08 + 1.39 + + + 17.08 + 1.77 + + + 17.08 + 2.15 + + + 17.08 + 2.53 + + + 17.08 + 2.91 + + + + + + + + 17.08 + 3.30 + + + 17.02 + 3.67 + + + 16.71 + 3.90 + + + 15.3 + 2.68 + + + 15.3 + 2.30 + + + 15.28 + 1.92 + + + 15.29 + 1.54 + + + 15.32 + 1.16 + + + 11.77 + 2.05 + + + 12.15 + 2.05 + + + 12.53 + 2.03 + + + 12.91 + 1.99 + + + 13.24 + 1.80 + + + 13.55 + 1.58 + + + 13.85 + 1.35 + + + 14.14 + 1.11 + + + 14.32 + 4.02 + + + 13.94 + 4.01 + + + 13.56 + 4.01 + + + 13.18 + 4.01 + + + 12.8 + 4.01 + + + 12.42 + 4.01 + + + 12.04 + 4.01 + + + 11.66 + 4.00 + + + 11.28 + 4.00 + + + 10.9 + 4.00 + + + 10.51 + 4.01 + + + 10.13 + 4.01 + + + 9.75 + 4.01 + + + 9.37 + 4.01 + + + 8.99 + 4.01 + + + 8.61 + 4.01 + + + 8.23 + 4.01 + + + 7.85 + 4.01 + + + 7.47 + 4.01 + + + 7.09 + 4.01 + + + 6.71 + 4.05 + + + 6.64 + 4.33 + + + 6.64 + 4.71 + + + 6.64 + 5.09 + + + 6.78 + 5.44 + + + 7.0 + 5.75 + + + 7.01 + 6.13 + + + 6.95 + 6.51 + + + 6.7 + 6.80 + + + 6.33 + 6.89 + + + 5.95 + 6.91 + + + 7.02 + 4.38 + + + 7.0 + 4.76 + + + 6.99 + 5.15 + + + 15.66 + 4.98 + + + 15.66 + 5.36 + + + 15.66 + 5.74 + + + 15.67 + 6.12 + + + 15.67 + 6.50 + + + 15.68 + 6.88 + + + 15.88 + 7.21 + + + 16.1 + 7.52 + + + 16.21 + 7.88 + + + 16.25 + 8.26 + + + 16.24 + 8.64 + + + 16.25 + 9.02 + + + 16.25 + 9.40 + + + 15.87 + 9.45 + + + 15.87 + 9.08 + + + 15.87 + 8.70 + + + 15.87 + 8.32 + + + 15.86 + 7.94 + + + 15.74 + 7.58 + + + 15.54 + 7.26 + + + 15.36 + 6.93 + + + 15.29 + 6.55 + + + 15.3 + 6.17 + + + 15.31 + 5.80 + + + 15.3 + 5.42 + + + 15.28 + 5.04 + + + 16.25 + 9.90 + + + 16.52 + 10.17 + + + 16.81 + 10.42 + + + 16.72 + 10.79 + + + 16.46 + 11.07 + + + 16.11 + 11.22 + + + 15.74 + 11.13 + + + 15.45 + 10.88 + + + 15.4 + 10.51 + + + 15.53 + 10.15 + + + 15.8 + 9.89 + + + 16.32 + 11.43 + + + 16.28 + 11.81 + + + 16.26 + 12.19 + + + 16.28 + 12.57 + + + 16.48 + 12.89 + + + 16.83 + 13.02 + + + 17.21 + 13.03 + + + 17.59 + 13.02 + + + 17.97 + 13.02 + + + 18.35 + 13.02 + + + 18.73 + 13.02 + + + 19.11 + 13.02 + + + 19.49 + 13.00 + + + 19.78 + 12.76 + + + 19.94 + 12.42 + + + 19.94 + 12.04 + + + 19.94 + 11.66 + + + 19.94 + 11.28 + + + 19.78 + 10.93 + + + 19.43 + 10.77 + + + 19.06 + 10.74 + + + 18.67 + 10.74 + + + 18.29 + 10.74 + + + 17.91 + 10.73 + + + 17.53 + 10.75 + + + 17.15 + 10.77 + + + 17.18 + 10.39 + + + 17.56 + 10.38 + + + 17.94 + 10.38 + + + 18.32 + 10.39 + + + 18.7 + 10.40 + + + 19.08 + 10.39 + + + 19.46 + 10.40 + + + 19.82 + 10.50 + + + 20.1 + 10.75 + + + 20.28 + 11.09 + + + 20.36 + 11.46 + + + 20.34 + 11.84 + + + 20.31 + 12.22 + + + 20.24 + 12.60 + + + 20.12 + 12.96 + + + 19.88 + 13.25 + + + 19.52 + 13.39 + + + 19.14 + 13.44 + + + 18.76 + 13.45 + + + 18.38 + 13.45 + + + 18.0 + 13.44 + + + 17.62 + 13.45 + + + 17.24 + 13.45 + + + 16.86 + 13.42 + + + 16.49 + 13.32 + + + 16.2 + 13.08 + + + 16.01 + 12.75 + + + 15.9 + 12.39 + + + 15.88 + 12.01 + + + 15.88 + 11.63 + + + 15.08 + 10.79 + + + 14.7 + 10.77 + + + 14.32 + 10.77 + + + 13.94 + 10.76 + + + 13.94 + 10.35 + + + 14.32 + 10.36 + + + 14.7 + 10.35 + + + 15.08 + 10.35 + + + 0.74 + 10.95 + + + 0.79 + 11.33 + + + 0.98 + 11.66 + + + 1.34 + 11.77 + + + 1.72 + 11.78 + + + 2.1 + 11.78 + + + 2.48 + 11.78 + + + 2.86 + 11.78 + + + 3.24 + 11.78 + + + 3.62 + 11.78 + + + 4.0 + 11.78 + + + 4.37 + 11.78 + + + 4.75 + 11.79 + + + 5.13 + 11.79 + + + 5.51 + 11.78 + + + 5.89 + 11.78 + + + 6.27 + 11.78 + + + 6.65 + 11.77 + + + 6.7 + 12.16 + + + 6.32 + 12.16 + + + 5.94 + 12.16 + + + 5.56 + 12.15 + + + 5.18 + 12.15 + + + 4.8 + 12.15 + + + 4.42 + 12.16 + + + 4.04 + 12.16 + + + 3.66 + 12.16 + + + 3.27 + 12.15 + + + 2.89 + 12.15 + + + 2.51 + 12.15 + + + 2.13 + 12.15 + + + 1.75 + 12.16 + + + 1.37 + 12.15 + + + 1.0 + 12.07 + + + 0.68 + 11.86 + + + 0.47 + 11.54 + + + 0.34 + 11.19 + + + 0.33 + 10.81 + + + 13.72 + 11.07 + + + 13.35 + 11.18 + + + 12.97 + 11.18 + + + 12.59 + 11.18 + + + 12.21 + 11.19 + + + + + + + + + 11.83 + 11.24 + + + 11.47 + 11.36 + + + 11.11 + 11.49 + + + + + + + + + + 10.78 + 11.68 + + + 10.45 + 11.87 + + + 10.11 + 12.05 + + + 9.77 + 12.22 + + + 9.41 + 12.36 + + + 9.05 + 12.47 + + + 8.68 + 12.55 + + + 8.3 + 12.60 + + + 7.92 + 12.59 + + + 7.54 + 12.58 + + + 7.16 + 12.57 + + + 6.9 + 11.50 + + + 7.24 + 11.32 + + + 7.62 + 11.34 + + + 8.0 + 11.34 + + + 8.38 + 11.33 + + + 8.75 + 11.27 + + + 9.12 + 11.18 + + + 9.46 + 11.03 + + + 9.81 + 10.88 + + + 10.15 + 10.71 + + + 10.48 + 10.52 + + + 10.81 + 10.33 + + + 11.15 + 10.18 + + + 11.52 + 10.08 + + + 11.89 + 10.00 + + + 12.27 + 9.98 + + + 12.64 + 9.96 + + + 13.02 + 9.94 + + + 13.4 + 9.94 + + + 13.6 + 9.94 + + + 13.78 + 9.98 + + + 13.56 + 10.75 + + + 13.18 + 10.75 + + + 12.8 + 10.74 + + + 12.42 + 10.77 + + + 12.04 + 10.81 + + + 11.66 + 10.87 + + + 11.3 + 10.99 + + + 10.95 + 11.14 + + + 10.61 + 11.32 + + + 10.28 + 11.51 + + + 9.94 + 11.68 + + + 9.6 + 11.86 + + + 9.25 + 12.01 + + + 8.88 + 12.11 + + + 8.5 + 12.15 + + + 8.13 + 12.20 + + + 7.74 + 12.20 + + + 7.36 + 12.20 + + + 6.98 + 12.20 + + + 7.03 + 11.76 + + + 7.41 + 11.76 + + + 7.78 + 11.77 + + + 8.16 + 11.76 + + + 8.54 + 11.75 + + + 8.91 + 11.66 + + + 9.27 + 11.54 + + + 9.62 + 11.41 + + + 9.96 + 11.23 + + + 10.3 + 11.06 + + + 10.64 + 10.90 + + + 10.97 + 10.72 + + + 11.33 + 10.59 + + + 11.69 + 10.48 + + + 12.06 + 10.42 + + + 12.44 + 10.37 + + + 12.82 + 10.37 + + + 13.2 + 10.37 + + + 13.58 + 10.36 + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + True + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + False + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + False + + + False + + + False + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + + + + + + False + + + False + + + True + + + True + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + True + + + True + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + + + + + + + + + + + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + + + + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + + + + + + + True + + True + + True + True + True + + True + + True + True + True + + True + + True + True + True + + True + + True + True + True + + True + + True + True + True + + True + + True + True + True + + True + + True + True + True + + True + + True + True + True + + True + + True + True + True + + True + + True + + True + + + + + True + + True + + True + True + + + + + + True + + True + + True + True + + + + + + + + + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + True + + True + + + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + + + \ No newline at end of file diff --git a/src/example/src/gercek2.graphml b/src/example/src/gercek2.graphml new file mode 100644 index 0000000..8228102 --- /dev/null +++ b/src/example/src/gercek2.graphml @@ -0,0 +1,3829 @@ + + + + + + + + + + + + + 9.34 + 0.54 + + + 10.04 + 0.54 + + + 10.99 + 0.54 + + + 11.71 + 0.54 + + + 12.41 + 0.54 + + + 9.34 + 1.3 + + + 10.03 + 1.3 + + + 10.73 + 1.3 + + + 11.41 + 1.3 + + + 13.11 + 1.3 + + + + + + 4.17 + 6.89 + + + 4.17 + 6.52 + + + 4.76 + 5.94 + + + 5.13 + 5.94 + + + 5.71 + 6.89 + + + 5.13 + 7.48 + + + 4.76 + 7.48 + + + 4.94 + 6.71 + + + 4.94 + 6.71 + + + 4.94 + 6.71 + + + 4.79 + 6.52 + + + 1.93 + 6.89 + + + 1.93 + 6.52 + + + 2.52 + 5.93 + + + 2.88 + 5.93 + + + 3.47 + 6.52 + + + 3.47 + 6.89 + + + 2.7 + 6.70 + + + 2.7 + 6.70 + + + 2.7 + 6.70 + + + 2.7 + 6.70 + + + 1.93 + 4.02 + + + 1.93 + 3.65 + + + 2.51 + 3.07 + + + 2.88 + 3.07 + + + 3.47 + 3.65 + + + 3.47 + 4.02 + + + 2.88 + 4.61 + + + 2.51 + 4.61 + + + 2.7 + 3.84 + + + 2.7 + 3.84 + + + 2.7 + 3.84 + + + 2.7 + 3.84 + + + 4.17 + 4.02 + + + 4.17 + 3.65 + + + 4.75 + 3.06 + + + 5.12 + 3.06 + + + 5.71 + 4.02 + + + 5.12 + 4.60 + + + 4.75 + 4.60 + + + 4.94 + 3.83 + + + 4.94 + 3.83 + + + 4.94 + 3.83 + + + 4.94 + 3.83 + + + 14.71 + 4.01 + + + 14.71 + 3.64 + + + 15.29 + 3.05 + + + 16.25 + 4.01 + + + 15.66 + 4.60 + + + 15.29 + 4.60 + + + 15.48 + 3.83 + + + 15.48 + 3.83 + + + 15.48 + 3.83 + + + 15.48 + 3.83 + + + 1.94 + 0.36 + + + 1.94 + 0.73 + + + 2.51 + 1.30 + + + 2.88 + 1.30 + + + 3.45 + 0.36 + + + 3.45 + 0.73 + + + 2.69 + 0.54 + + + 2.69 + 0.54 + + + 2.69 + 0.54 + + + 0.35 + 10.57 + + + 0.72 + 10.57 + + + 1.29 + 10.00 + + + 1.29 + 9.63 + + + 0.35 + 9.06 + + + 0.72 + 9.06 + + + 0.53 + 9.81 + + + 0.53 + 9.81 + + + 0.53 + 9.81 + + + 0.35 + 7.48 + + + 0.72 + 7.48 + + + 1.29 + 6.90 + + + 1.29 + 6.54 + + + 0.35 + 5.96 + + + 0.72 + 5.96 + + + 0.53 + 6.72 + + + 0.53 + 6.72 + + + 0.53 + 6.72 + + + 0.35 + 4.59 + + + 0.72 + 4.59 + + + 1.29 + 4.02 + + + 1.29 + 3.65 + + + 0.35 + 3.08 + + + 0.72 + 3.08 + + + 0.53 + 3.84 + + + 0.53 + 3.84 + + + 0.53 + 3.84 + + + 2.88 + 1.68 + + + 2.88 + 2.06 + + + 2.88 + 2.44 + + + 2.89 + 2.82 + + + 2.51 + 2.69 + + + 2.51 + 2.31 + + + 2.52 + 1.93 + + + 2.51 + 1.55 + + + 1.56 + 0.72 + + + 1.18 + 0.76 + + + 0.87 + 0.98 + + + 0.73 + 1.34 + + + 0.73 + 1.72 + + + 0.73 + 2.10 + + + 0.73 + 2.48 + + + 0.74 + 2.86 + + + 0.35 + 2.70 + + + 0.35 + 2.32 + + + 0.34 + 1.94 + + + 0.34 + 1.56 + + + 0.39 + 1.19 + + + 0.51 + 0.83 + + + 0.77 + 0.56 + + + 1.11 + 0.39 + + + 1.48 + 0.32 + + + 3.79 + 4.02 + + + 3.85 + 3.65 + + + 1.55 + 4.02 + + + 1.67 + 3.65 + + + 2.88 + 4.99 + + + 2.88 + 5.37 + + + 2.88 + 5.75 + + + 2.52 + 5.55 + + + 2.52 + 5.17 + + + 2.52 + 4.79 + + + 0.73 + 4.97 + + + 0.74 + 5.35 + + + 0.74 + 5.73 + + + 0.35 + 5.59 + + + 0.34 + 5.21 + + + 0.35 + 4.83 + + + 1.67 + 6.53 + + + 1.55 + 6.88 + + + 3.85 + 6.52 + + + 3.79 + 6.88 + + + 5.13 + 7.86 + + + 5.11 + 8.24 + + + 5.04 + 8.61 + + + 4.9 + 8.97 + + + 4.71 + 9.30 + + + 4.46 + 9.59 + + + 4.17 + 9.84 + + + 3.84 + 10.03 + + + 3.48 + 10.14 + + + 3.1 + 10.19 + + + 2.72 + 10.19 + + + 2.35 + 10.11 + + + 1.97 + 10.03 + + + 1.59 + 10.01 + + + 1.67 + 9.61 + + + 2.05 + 9.62 + + + 2.4 + 9.75 + + + 2.77 + 9.84 + + + 3.15 + 9.83 + + + 3.52 + 9.74 + + + 3.87 + 9.59 + + + 4.17 + 9.37 + + + 4.42 + 9.08 + + + 4.64 + 8.78 + + + 4.73 + 8.41 + + + 4.76 + 8.04 + + + 4.76 + 7.66 + + + 0.72 + 7.86 + + + 0.73 + 8.24 + + + 0.73 + 8.62 + + + 0.34 + 8.68 + + + 0.34 + 8.30 + + + 0.34 + 7.92 + + + 3.83 + 0.35 + + + 4.21 + 0.35 + + + 4.59 + 0.35 + + + 4.97 + 0.35 + + + 5.34 + 0.35 + + + 5.72 + 0.35 + + + 6.1 + 0.36 + + + 6.44 + 0.54 + + + 6.65 + 0.86 + + + 6.66 + 1.24 + + + 6.63 + 1.62 + + + 6.62 + 2.00 + + + 6.63 + 2.38 + + + 6.63 + 2.76 + + + 6.62 + 3.14 + + + 6.61 + 3.52 + + + 6.71 + 4.05 + + + 6.1 + 4.02 + + + 4.76 + 2.68 + + + 4.76 + 2.31 + + + 4.76 + 1.93 + + + 4.75 + 1.55 + + + 4.75 + 1.17 + + + 4.8 + 0.67 + + + 4.35 + 0.77 + + + 3.97 + 0.75 + + + 4.8 + 0.44 + + + 5.07 + 0.93 + + + 5.14 + 1.31 + + + 5.16 + 1.69 + + + 5.15 + 2.07 + + + 5.12 + 2.45 + + + 5.14 + 2.83 + + + 5.13 + 4.98 + + + 5.13 + 5.37 + + + 5.14 + 5.75 + + + 4.75 + 5.56 + + + 4.75 + 5.18 + + + 4.73 + 4.80 + + + 7.03 + 0.89 + + + 7.41 + 0.90 + + + 7.79 + 0.91 + + + 8.17 + 0.90 + + + 8.54 + 0.90 + + + 8.92 + 0.90 + + + 9.3 + 0.90 + + + 9.68 + 0.91 + + + 10.06 + 0.91 + + + 10.44 + 0.92 + + + 10.82 + 0.92 + + + 11.2 + 0.93 + + + 11.58 + 0.93 + + + 11.95 + 0.93 + + + 12.33 + 0.93 + + + 12.71 + 0.93 + + + 13.09 + 0.92 + + + 13.47 + 0.92 + + + 13.85 + 0.92 + + + 14.23 + 0.92 + + + 14.61 + 0.93 + + + 14.99 + 0.94 + + + 15.36 + 0.94 + + + 15.74 + 0.94 + + + 16.12 + 0.94 + + + 16.5 + 0.92 + + + 16.84 + 1.10 + + + 17.08 + 1.39 + + + 17.08 + 1.77 + + + 17.08 + 2.15 + + + 17.08 + 2.53 + + + 17.08 + 2.91 + + + 17.08 + 3.30 + + + 17.02 + 3.67 + + + 16.71 + 3.90 + + + 15.3 + 2.68 + + + 15.3 + 2.30 + + + 15.28 + 1.92 + + + 15.29 + 1.54 + + + 15.32 + 1.16 + + + 11.77 + 2.05 + + + 12.15 + 2.05 + + + 12.53 + 2.03 + + + 12.91 + 1.99 + + + 13.24 + 1.80 + + + 13.55 + 1.58 + + + 13.85 + 1.35 + + + 14.14 + 1.11 + + + 14.32 + 4.02 + + + 13.94 + 4.01 + + + 13.56 + 4.01 + + + 13.18 + 4.01 + + + 12.8 + 4.01 + + + 12.42 + 4.01 + + + 12.04 + 4.01 + + + 11.66 + 4.00 + + + 11.28 + 4.00 + + + 10.9 + 4.00 + + + 10.51 + 4.01 + + + 10.13 + 4.01 + + + 9.75 + 4.01 + + + 9.37 + 4.01 + + + 8.99 + 4.01 + + + 8.61 + 4.01 + + + 8.23 + 4.01 + + + 7.85 + 4.01 + + + 7.47 + 4.01 + + + 7.09 + 4.01 + + + 6.71 + 4.05 + + + 6.64 + 4.33 + + + 6.64 + 4.71 + + + 6.64 + 5.09 + + + 6.78 + 5.44 + + + 7.0 + 5.75 + + + 7.01 + 6.13 + + + 6.95 + 6.51 + + + 6.7 + 6.80 + + + 6.33 + 6.89 + + + 5.95 + 6.91 + + + 7.02 + 4.38 + + + 7.0 + 4.76 + + + 6.99 + 5.15 + + + 15.66 + 4.98 + + + 15.66 + 5.36 + + + 15.66 + 5.74 + + + 15.67 + 6.12 + + + 15.67 + 6.50 + + + 15.68 + 6.88 + + + 15.88 + 7.21 + + + 16.1 + 7.52 + + + 16.21 + 7.88 + + + 16.25 + 8.26 + + + 16.24 + 8.64 + + + 16.25 + 9.02 + + + 16.25 + 9.40 + + + 15.87 + 9.45 + + + 15.87 + 9.08 + + + 15.87 + 8.70 + + + 15.87 + 8.32 + + + 15.86 + 7.94 + + + 15.74 + 7.58 + + + 15.54 + 7.26 + + + 15.36 + 6.93 + + + 15.29 + 6.55 + + + 15.3 + 6.17 + + + 15.31 + 5.80 + + + 15.3 + 5.42 + + + 15.28 + 5.04 + + + 16.25 + 9.90 + + + 16.52 + 10.17 + + + 16.81 + 10.42 + + + 16.72 + 10.79 + + + 16.46 + 11.07 + + + 16.11 + 11.22 + + + 15.74 + 11.13 + + + 15.45 + 10.88 + + + 15.4 + 10.51 + + + 15.53 + 10.15 + + + 15.8 + 9.89 + + + 16.32 + 11.43 + + + 16.28 + 11.81 + + + 16.26 + 12.19 + + + 16.28 + 12.57 + + + 16.48 + 12.89 + + + 16.83 + 13.02 + + + 17.21 + 13.03 + + + 17.59 + 13.02 + + + 17.97 + 13.02 + + + 18.35 + 13.02 + + + 18.73 + 13.02 + + + 19.11 + 13.02 + + + 19.49 + 13.00 + + + 19.78 + 12.76 + + + 19.94 + 12.42 + + + 19.94 + 12.04 + + + 19.94 + 11.66 + + + 19.94 + 11.28 + + + 19.78 + 10.93 + + + 19.43 + 10.77 + + + 19.06 + 10.74 + + + 18.67 + 10.74 + + + 18.29 + 10.74 + + + 17.91 + 10.73 + + + 17.53 + 10.75 + + + 17.15 + 10.77 + + + 17.18 + 10.39 + + + 17.56 + 10.38 + + + 17.94 + 10.38 + + + 18.32 + 10.39 + + + 18.7 + 10.40 + + + 19.08 + 10.39 + + + 19.46 + 10.40 + + + 19.82 + 10.50 + + + 20.1 + 10.75 + + + 20.28 + 11.09 + + + 20.36 + 11.46 + + + 20.34 + 11.84 + + + 20.31 + 12.22 + + + 20.24 + 12.60 + + + 20.12 + 12.96 + + + 19.88 + 13.25 + + + 19.52 + 13.39 + + + 19.14 + 13.44 + + + 18.76 + 13.45 + + + 18.38 + 13.45 + + + 18.0 + 13.44 + + + 17.62 + 13.45 + + + 17.24 + 13.45 + + + 16.86 + 13.42 + + + 16.49 + 13.32 + + + 16.2 + 13.08 + + + 16.01 + 12.75 + + + 15.9 + 12.39 + + + 15.88 + 12.01 + + + 15.88 + 11.63 + + + 15.08 + 10.79 + + + 14.7 + 10.77 + + + 14.32 + 10.77 + + + 13.94 + 10.76 + + + 13.94 + 10.35 + + + 14.32 + 10.36 + + + 14.7 + 10.35 + + + 15.08 + 10.35 + + + 0.74 + 10.95 + + + 0.79 + 11.33 + + + 0.98 + 11.66 + + + 1.34 + 11.77 + + + 1.72 + 11.78 + + + 2.1 + 11.78 + + + 2.48 + 11.78 + + + 2.86 + 11.78 + + + 3.24 + 11.78 + + + 3.62 + 11.78 + + + 4.0 + 11.78 + + + 4.37 + 11.78 + + + 4.75 + 11.79 + + + 5.13 + 11.79 + + + 5.51 + 11.78 + + + 5.89 + 11.78 + + + 6.27 + 11.78 + + + 6.65 + 11.77 + + + 6.7 + 12.16 + + + 6.32 + 12.16 + + + 5.94 + 12.16 + + + 5.56 + 12.15 + + + 5.18 + 12.15 + + + 4.8 + 12.15 + + + 4.42 + 12.16 + + + 4.04 + 12.16 + + + 3.66 + 12.16 + + + 3.27 + 12.15 + + + 2.89 + 12.15 + + + 2.51 + 12.15 + + + 2.13 + 12.15 + + + 1.75 + 12.16 + + + 1.37 + 12.15 + + + 1.0 + 12.07 + + + 0.68 + 11.86 + + + 0.47 + 11.54 + + + 0.34 + 11.19 + + + 0.33 + 10.81 + + + 13.72 + 11.07 + + + 13.35 + 11.18 + + + 12.97 + 11.18 + + + 12.59 + 11.18 + + + 12.21 + 11.19 + + + 11.83 + 11.24 + + + 11.47 + 11.36 + + + 11.11 + 11.49 + + + 10.78 + 11.68 + + + 10.45 + 11.87 + + + 10.11 + 12.05 + + + 9.77 + 12.22 + + + 9.41 + 12.36 + + + 9.05 + 12.47 + + + 8.68 + 12.55 + + + 8.3 + 12.60 + + + 7.92 + 12.59 + + + 7.54 + 12.58 + + + 7.16 + 12.57 + + + 6.9 + 11.50 + + + 7.24 + 11.32 + + + 7.62 + 11.34 + + + 8.0 + 11.34 + + + 8.38 + 11.33 + + + 8.75 + 11.27 + + + 9.12 + 11.18 + + + 9.46 + 11.03 + + + 9.81 + 10.88 + + + 10.15 + 10.71 + + + 10.48 + 10.52 + + + 10.81 + 10.33 + + + 11.15 + 10.18 + + + 11.52 + 10.08 + + + 11.89 + 10.00 + + + 12.27 + 9.98 + + + 12.64 + 9.96 + + + 13.02 + 9.94 + + + 13.4 + 9.94 + + + 13.78 + 9.98 + + + 13.56 + 10.75 + + + 13.18 + 10.75 + + + 12.8 + 10.74 + + + 12.42 + 10.77 + + + 12.04 + 10.81 + + + 11.66 + 10.87 + + + 11.3 + 10.99 + + + 10.95 + 11.14 + + + 10.61 + 11.32 + + + 10.28 + 11.51 + + + 9.94 + 11.68 + + + 9.6 + 11.86 + + + 9.25 + 12.01 + + + 8.88 + 12.11 + + + 8.5 + 12.15 + + + 8.13 + 12.20 + + + 7.74 + 12.20 + + + 7.36 + 12.20 + + + 6.98 + 12.20 + + + 7.03 + 11.76 + + + 7.41 + 11.76 + + + 7.78 + 11.77 + + + 8.16 + 11.76 + + + 8.54 + 11.75 + + + 8.91 + 11.66 + + + 9.27 + 11.54 + + + 9.62 + 11.41 + + + 9.96 + 11.23 + + + 10.3 + 11.06 + + + 10.64 + 10.90 + + + 10.97 + 10.72 + + + 11.33 + 10.59 + + + 11.69 + 10.48 + + + 12.06 + 10.42 + + + 12.44 + 10.37 + + + 12.82 + 10.37 + + + 13.2 + 10.37 + + + 13.58 + 10.36 + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + True + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + False + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + False + + + False + + + False + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + + + + + + + False + + + False + + + True + + + True + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + True + + + True + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + + + + + + + + + + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + + True + + + \ No newline at end of file diff --git a/src/example/src/mpc.py b/src/example/src/mpc.py new file mode 100755 index 0000000..7bf79cd --- /dev/null +++ b/src/example/src/mpc.py @@ -0,0 +1,1839 @@ +#!/usr/bin/env python3 + +import rospy +import cv2 +import numpy as np +from cv_bridge import CvBridge, CvBridgeError +import math +from math import atan2 +import json +import xml.etree.ElementTree as ET +from casadi import sin, cos, pi +import casadi as ca +import sys +import time +import random +from std_msgs.msg import String, Bool, Header, Byte +from sensor_msgs.msg import Image, Imu +from geometry_msgs.msg import PoseWithCovarianceStamped, Quaternion +from utils.msg import localisation, IMU +from nav_msgs.msg import Odometry +import numpy as np + +from example.msg import MekatronomYolo +import os +import tf +import tf + +from obstacle_detector.msg import Obstacles +class MPC(): + + def __init__(self): + + + #Parameters + self.debug = rospy.get_param('~debug', True) + self.hue_min = rospy.get_param('~hue_min', 0) + self.hue_max = rospy.get_param('~hue_max', 60) + self.sat_min = rospy.get_param('~sat_min', 0) + self.sat_max = rospy.get_param('~sat_max', 255) + self.val_min = rospy.get_param('~val_min', 70) + self.val_max = rospy.get_param('~val_max', 255) + # self.graphml_file_path = rospy.get_param('~graphml_file_path', '/home/mekatronom/boschmekatronom_ws/BoschMekatronom/src/example/src/Competition_track_graph.graphml') + self.PathSituations = False + self.mpc_started = False + self.localisation_CB = False + self.IMU_cb = False + + #For checking behaviour tree + self.last_update_time = rospy.Time.now() + self.last_update_time_obstacles_checking = rospy.Time.now() + self.crosswalk_update_time = rospy.Time.now() + self.last_time_went_node = rospy.Time.now() + self.last_path_index = 0 + self.last_target_update_time = None + self.current_target = None + + self.yolo_data = MekatronomYolo() + + self.state = "keep lane" + + + self.intercept_targets = ["243","261","252","54","51","56","317","367","397","404","34","84","163","80","93","176", + "91","14","18","2","6","143","102","32","16","222","219","30","38","100","26","42", + "109","113","121","75","185","71","40","205","226","283","198"] + + self.dont_check_obstacles_here = ["228","229","230","231","232","233","234","235","236","237","238","239","240"] + # self.parking_secture_nodes_id = ["450","451","452","453","454","455","456","457","458","459","460","461","462","463","464","465","466","467"] + + # self.intercept_targets = [] + self.distance_flag = False + self.yolo_intercept_flag = False + self.park_scenerio = False + self.parking_flag = False + self.crosswalk_scenerio = False + self.crosswalk_flag = False + self.motorway_flag = False + self.motorway_scenerio = False + + self.prev_time = rospy.Time.now().to_sec() + self.bridge = CvBridge() + + #sükrü pathfinding icin + self.source_node="263" #472 + self.target_node="225" #197 #901 + self.current_id=self.source_node + self.current_id_original=self.source_node + #newPathFinding + self.graphml_file_path = os.path.expanduser( + rospy.get_param( + '~graphml_file_path', + '~/autonomus_ws/src/autonomus_vehicle/src/example/graphml/fixed2.graphml' + ) + ) + + self.file_path_original = os.path.expanduser( + '~/autonomus_ws/src/autonomus_vehicle/src/example/graphml/gercek2.graphml' + ) + self.callnumber=0 + self.new_point_ctr=600 + self.obs_dontuse = ["273"] + self.yolvar=False + self.parking_nodes_id = ["900","901","902","903","904","910","911","912","913","914"] + self.traffic_light_nodes_id = ["32","121","135"] + self.parking_spot_is_full = [] + self.parkings_are_available =[] + self.expath = [] + + self.flagsolla = 0 + self.obstaclesayac = 0 + self.center_x = [] + self.center_y = [] + self.obstacles_array = [] + self.past_obs_dontuse = [] + self.traffic_light_master = [] + self.traffic_light_slave = [] + self.traffic_light_start = [] + self.pathGoalsYawDegreecalled = False + + + self.process_and_publish_data(self.source_node,self.target_node)#burda çağırılmış ilk defa her çıkmaz sokakta yine çağırıcam + + ##############################3sükrü + #ForPathFinding + self.file_path = rospy.get_param('~file_path', self.graphml_file_path) + self.data_pub = rospy.Publisher('~graph_data', String, queue_size=10) + + # self.bridge = CvBridge() + self.cv_image = np.zeros((640, 480)) + + #Subscribers + self.image_sub = rospy.Subscriber('/automobile/image_raw', Image, self.image_callback) + self.localisation_sub = rospy.Subscriber('/odometry/filtered', Odometry, self.localisation_callback) + self.imu_sub = rospy.Subscriber('/automobile/IMU', Imu, self.imu_callback) + self.obstacleDetector_sub = rospy.Subscriber('/obstacles', Obstacles, self.obstacleDetector_callback) + self.yolo_intercept_sub = rospy.Subscriber('/automobile/yolo', MekatronomYolo, self.yolo_intercept_callback) + self.traffic_light_master_sub = rospy.Subscriber('/automobile/trafficlight/master', Byte, self.traffic_light_master_callback) + self.traffic_light_slave_sub = rospy.Subscriber('/automobile/trafficlight/slave', Byte, self.traffic_light_slave_callback) + self.traffic_light_start_sub = rospy.Subscriber('/automobile/trafficlight/start', Byte, self.traffic_light_start_callback) + #Publishers + + self.carControl = rospy.Publisher('/automobile/command', String, queue_size=2, latch=True) + + # self.imu_pub = rospy.Publisher('/imu0', Imu, queue_size=10) #the sensor_msgs imu + # self.localisation_pub = rospy.Publisher('/localisation0', PoseWithCovarianceStamped, queue_size=10) #the sensor_msgs localisation + + + #timerCB + self.behaviourTimer = rospy.Timer(rospy.Duration(0.5), self.behaviourTimerCallback) + self.mpcTimer = rospy.Timer(rospy.Duration(0.05), self.mpcTimerCallback) + + + + def DM2Arr(self,dm): + return np.array(dm.full()) + + def shift_timestep(self): + + self.state_init = ca.DM([self.position_x, self.position_y, self.yaw_rad]) + f_value = self.f(self.state_init,self.u[0, :].T) + self.next_state = ca.DM.full(self.state_init + (self.step_horizon * f_value)) + self.t0 = self.t0 + self.step_horizon + self.u0 = ca.vertcat(self.u[1:, :], self.u[-1, :]) + + def mpc_start_settings(self): + # print("mpcsettings") + + if self.park_scenerio: + rospy.loginfo("SETTINGS TO PARK PARAMS FOR MPC") + + Q_x = rospy.get_param('~Q_x', 1) + Q_y = rospy.get_param('~Q_y', 4) + Q_theta = rospy.get_param('~Q_theta', 0.05) + R1 = rospy.get_param('~R1', 0.01) + R2 = rospy.get_param('~R2', 0.02) + step_horizon = rospy.get_param('~step_horizon', 0.1) + N = rospy.get_param('~N', 6) + rob_diam = rospy.get_param('~rob_diam', 0.354) + wheel_radius = rospy.get_param('~wheel_radius', 1) + L_value = rospy.get_param('~Lx', 0.3) + Ly = rospy.get_param('~Ly', 0.04) + v_max = rospy.get_param('~v_max', 0.2) + v_min = rospy.get_param('~v_min', -0.25) + omega_max = rospy.get_param('~omega_max', pi/7.5) #degree + omega_min = rospy.get_param('~omega_min', -pi/7.5) #degree + self.iteration = 0 + self.steeringRad = 0.0 + + elif self.crosswalk_scenerio: + rospy.loginfo("SETTINGS TO CROSSWALK PARAMS FOR MPC") + + Q_x = rospy.get_param('~Q_x', 1) + Q_y = rospy.get_param('~Q_y', 1) + Q_theta = rospy.get_param('~Q_theta', 1.0) + R1 = rospy.get_param('~R1', 1.0) + R2 = rospy.get_param('~R2', 1.8) + step_horizon = rospy.get_param('~step_horizon', 0.1) + N = rospy.get_param('~N', 12) + rob_diam = rospy.get_param('~rob_diam', 0.354) + wheel_radius = rospy.get_param('~wheel_radius', 1) + L_value = rospy.get_param('~Lx', 0.3) + Ly = rospy.get_param('~Ly', 0.04) + v_max = rospy.get_param('~v_max', 0.13) + v_min = rospy.get_param('~v_min', -0.2) + omega_max = rospy.get_param('~omega_max', pi/7.5) #degree + omega_min = rospy.get_param('~omega_min', -pi/7.5) #degree + self.iteration = 0 + self.steeringRad = 0.0 + + elif self.motorway_scenerio: + rospy.loginfo("INITIAL KEEP LANE PARAMS FOR MPC") + + Q_x = rospy.get_param('~Q_x', 1) + Q_y = rospy.get_param('~Q_y', 1) + Q_theta = rospy.get_param('~Q_theta', 2) + R1 = rospy.get_param('~R1', 2.0) + R2 = rospy.get_param('~R2', 2.8) + step_horizon = rospy.get_param('~step_horizon', 0.2) + N = rospy.get_param('~N', 4) + rob_diam = rospy.get_param('~rob_diam', 0.354) + wheel_radius = rospy.get_param('~wheel_radius', 1) + L_value = rospy.get_param('~Lx', 0.5) + Ly = rospy.get_param('~Ly', 0.04) + v_max = rospy.get_param('~v_max', 0.5) + v_min = rospy.get_param('~v_min', -0.1) + omega_max = rospy.get_param('~omega_max', pi/7.5) #degree + omega_min = rospy.get_param('~omega_min', -pi/7.5) #degree + self.iteration = 0 + self.steeringRad = 0.0 + + else: + rospy.loginfo("INITIAL KEEP LANE PARAMS FOR MPC") + + Q_x = rospy.get_param('~Q_x', 1) + Q_y = rospy.get_param('~Q_y', 1) + Q_theta = rospy.get_param('~Q_theta', 1) + R1 = rospy.get_param('~R1', 1.0) + R2 = rospy.get_param('~R2', 1.4) + step_horizon = rospy.get_param('~step_horizon', 0.2) + N = rospy.get_param('~N', 12) + rob_diam = rospy.get_param('~rob_diam', 0.354) + wheel_radius = rospy.get_param('~wheel_radius', 1) + L_value = rospy.get_param('~Lx', 0.4) + Ly = rospy.get_param('~Ly', 0.04) + v_max = rospy.get_param('~v_max', 0.33) + v_min = rospy.get_param('~v_min', -0.1) + omega_max = rospy.get_param('~omega_max', pi/7.5) #degree + omega_min = rospy.get_param('~omega_min', -pi/7.5) #degree + self.iteration = 0 + self.steeringRad = 0.0 + + rotationMatrix = np.array([[cos(self.yaw_rad),-sin(self.yaw_rad)], + [sin(self.yaw_rad),cos(self.yaw_rad)]]) + matrix = np.array([1,4]) + self.Q_x,self.Q_y = np.dot(rotationMatrix,matrix) + + nodes_x = np.array([node[1] for node in self.pathGoalsYawDegreeCopy]) + nodes_y = np.array([node[2] for node in self.pathGoalsYawDegreeCopy]) + + distances = np.sqrt((nodes_x - self.position_x)**2 + (nodes_y - self.position_y)**2) + min_distance, index = min((val, idx) for (idx, val) in enumerate(distances)) + closest_node_id = self.pathGoalsYawDegreeCopy[index][0] + self.last_path_index = index + + if self.park_scenerio: + current_id = self.current_id + print("parking scenerio") + + else: + current_id = closest_node_id + + matching_pairs = [pair for pair in self.SourceTargetNodesCopy if pair[0] == current_id] + next_id = matching_pairs[0][1] + matching_entry = next((entry for entry in self.pathGoalsYawDegreeCopy if entry[0] == next_id), None) + + target_x, target_y = matching_entry[1], matching_entry[2] + target_y = target_y + + dx = target_x - self.position_x + dy = target_y - self.position_y + yaw = atan2(dy, dx) + + self.goal_id = matching_entry + state_init = ca.DM([self.position_x, self.position_y, self.yaw_rad]) # initial state + state_target = ca.DM([target_x, target_y, yaw]) + + print("state_init",state_init) + print("state_target",state_target) + + x = ca.SX.sym('x') + y = ca.SX.sym('y') + theta = ca.SX.sym('theta') + states = ca.vertcat( + x, + y, + theta + ) + n_states = states.numel() + + v = ca.SX.sym('v') + omega = ca.SX.sym('omega') + controls = ca.vertcat( + v, + omega + ) + n_controls = controls.numel() + + # matrix containing all states over all time steps +1 (each column is a state vector) + X = ca.SX.sym('X', n_states, (N + 1)) + + # matrix containing all control actions over all time steps (each column is an action vector) + U = ca.SX.sym('U', n_controls, N) + + # coloumn vector for storing initial state and target state + P = ca.SX.sym('P', n_states + n_states) + + # state weights matrix (Q_X, Q_Y, Q_THETA) + Q = ca.diagcat(Q_x, Q_y, Q_theta) + + # controls weights matrix + R = ca.diagcat(R1, R2) + + # discretization model (e.g. x2 = f(x1, v, t) = x1 + v * dt) + rot_3d_z = ca.vertcat( + ca.horzcat(cos(theta), -sin(theta), 0), + ca.horzcat(sin(theta), cos(theta), 0), + ca.horzcat( 0, 0, 1) + ) + # Mecanum wheel transfer function which can be found here: + # https://www.researchgate.net/publication/334319114_Model_Predictive_Control_for_a_Mecanum-wheeled_robot_in_Dynamical_Environments + # J = (wheel_radius/4) * ca.DM([ + # [ 1, 1, 1, 1], + # [ -1, 1, 1, -1], + # [-1/(Lx+Ly), 1/(Lx+Ly), -1/(Lx+Ly), 1/(Lx+Ly)] + # ]) + # RHS = states + J @ controls * step_horizon # Euler discretization + #RHS = rot_3d_z @ J @ controls + + L = ca.SX.sym('L') + + RHS = ca.vertcat( + v * ca.cos(theta), + v * ca.sin(theta), + v / L_value * ca.tan(omega) + ) + # maps controls from [va, vb, vc, vd].T to [vx, vy, omega].T + f = ca.Function('f', [states, controls], [RHS]) + + + cost_fn = 0 # cost function + g = X[:, 0] - P[:n_states] # constraints in the equation + + st = X[:,0] + + + for k in range(N): + st = X[:, k] + con = U[:, k] + + cost_fn = cost_fn + ca.mtimes(ca.mtimes((st - P[3:6]).T, Q), (st - P[3:6])) + ca.mtimes(ca.mtimes(con.T, R), con) + + st_next = X[:, k+1] + #print("st_next",st_next) + k1 = f(st, con) + k2 = f(st + (step_horizon/2)*k1, con) + k3 = f(st + (step_horizon/2)*k2, con) + k4 = f(st + step_horizon * k3, con) + + + st_next_RK4 = st + (step_horizon / 6) * (k1 + 2 * k2 + 2 * k3 + k4) + #print("st_next_RK4",st_next_RK4) + + g = ca.vertcat(g, st_next - st_next_RK4) + + obs_x = -3.0 + obs_y = -3.0 + obs_diam = 0.3 + #print(g.shape) + + OPT_variables = ca.vertcat( # Example: 3x11 ---> 33x1 where 3=states, 11=N+1 + ca.reshape(X,3*(N+1),1), + ca.reshape(U,2*N, 1) + ) + #print("opt_Variables",OPT_variables) + + nlp_prob = { + 'f': cost_fn, + 'x': OPT_variables, + 'g': g, + 'p': P + } + + opts = { + 'ipopt': { + 'max_iter': 100, + 'print_level': 0, + 'acceptable_tol': 1e-8, + 'acceptable_obj_change_tol': 1e-6 + }, + 'print_time': 0 + } + + #print("nlp_prob",nlp_prob) + + solver = ca.nlpsol('solver', 'ipopt', nlp_prob, opts) + + + lbx = ca.DM.zeros((n_states*(N+1) + n_controls*N, 1)) + ubx = ca.DM.zeros((n_states*(N+1) + n_controls*N, 1)) + lbg = ca.DM.zeros((n_states*(N+1), 1)) + ubg = ca.DM.zeros((n_states*(N+1), 1)) + + # lbg[n_states*(N+1)+1: 3*(N+1) + (N+1)] = -ca.inf + # ubg[n_states*(N+1)+1: 3*(N+1) + (N+1)] = 0 + + lbx[0: n_states*(N+1): n_states] = -ca.inf # X lower bound + lbx[1: n_states*(N+1): n_states] = -ca.inf # Y lower bound + lbx[2: n_states*(N+1): n_states] = -ca.inf # theta lower bound + + ubx[0: n_states*(N+1): n_states] = ca.inf # X upper bound + ubx[1: n_states*(N+1): n_states] = ca.inf # Y upper bound + ubx[2: n_states*(N+1): n_states] = ca.inf # theta upper bound + + lbx[n_states*(N+1):n_states*(N+1)+2*N:2] = v_min # v lower bound for all V + ubx[n_states*(N+1):n_states*(N+1)+2*N:2] = v_max # v upper bound for all V + lbx[n_states*(N+1)+1:n_states*(N+1)+2*N:2] = omega_min # omega bound for all V + ubx[n_states*(N+1)+1:n_states*(N+1)+2*N:2] = omega_max # omega bound for all V + + + + args = { + 'lbg': lbg, # constraints lower bound + 'ubg': ubg, # constraints upper bound + 'lbx': lbx, + 'ubx': ubx + } + + # print("args",args) + self.t0 = 0 + + # print("ilk_target",state_target) + # print("ilk_konum",state_init) + + # xx = DM(state_init) + t = ca.DM(self.t0) + + u0 = ca.DM.zeros(N, 2) # initial control + X0 = ca.repmat(state_init, 1, N+1).T # initial state full + #print("u0",u0) + + cat_states = self.DM2Arr(X0) + #cat_controls = self.DM2Arr(u0[0, :]) + cat_controls = np.array([]).reshape(0, u0.shape[1]) + + self.times = np.array([[0]]) + + self.step_horizon = step_horizon + self.cat_controls = cat_controls + self.cat_states = cat_states + self.t = t + self.f = f + self.solver = solver + self.state_init =state_init + self.state_target = state_target + self.n_states = n_states + self.N = N + self.X0 =X0 + self.args = args + self.u0 = u0 + self.n_controls = n_controls + self.v_min = v_min + self.v_max = v_max + self.omega_min = omega_min + self.omega_max = omega_max + + + + def mpcTimerCallback(self,event): + try: + + if self.localisation_CB == True and self.IMU_cb == True: + + if not self.mpc_started: + print("mpc started") + self.mpc_start_settings() + self.mpc_started = True + print("mpc settings finished") + self.prev_time = rospy.Time.now().to_sec() + + + + self.args['p'] = ca.vertcat( + self.state_init, # current state + self.state_target # target state + ) + + self.args['x0'] = ca.vertcat( + ca.reshape(self.X0.T, self.n_states*(self.N+1), 1), + ca.reshape(self.u0.T, self.n_controls*self.N, 1) + ) + + sol = self.solver( + x0=self.args['x0'], + lbx=self.args['lbx'], + ubx=self.args['ubx'], + lbg=self.args['lbg'], + ubg=self.args['ubg'], + p=self.args['p'] + ) + + + + self.u = ca.reshape((sol['x'][self.n_states * (self.N + 1):]).T, self.n_controls, self.N).T + + print("\n\n\nstate_target",self.state_target) + + + self.cat_states = np.dstack(( + self.cat_states, + self.DM2Arr(self.X0) + )) + + self.cat_controls = np.vstack(( + self.cat_controls, + self.DM2Arr(self.u[0, :]) + )) + + self.shift_timestep() + + self.X0 = ca.reshape((sol['x'][: self.n_states * (self.N+1)]).T, self.n_states, self.N+1).T + # print("self.X0 ",self.X0) + + self.X0 = ca.vertcat( + self.X0[1:, :], + ca.reshape(self.X0[-1, :], 1, -1) + ) + + + if self.state == "keep lane": + self.steerAngle = math.degrees(float(self.DM2Arr(self.u[0, 1]))) + self.steerLateral = float(self.DM2Arr(self.u[0, 0])) + self.carControlPublisher() + + + state_target_slice = self.state_target[:2] + state_init_slice = self.state_init[:2] + + # Calculate the norm + distance = ca.norm_2(ca.DM(state_init_slice) - ca.DM(state_target_slice)) + + + if distance < 0.2: + #print(self.nodedatabest) + print("self.current_id",self.current_id) + #print(self.path_original) + + now=self.current_id_original + if self.nodedatabest[now]["solla"]: + self.flagsolla=1 + else: + self.flagsolla=0 + print("sollaaaaaa",self.flagsolla) + + #print("test3") + print("insideofdistance") + + if self.pathGoalsYawDegree: + nodes_x = np.array([node[1] for node in self.pathGoalsYawDegree]) + nodes_y = np.array([node[2] for node in self.pathGoalsYawDegree]) + distances = np.sqrt((nodes_x - self.position_x)**2 + (nodes_y - self.position_y)**2) + min_distance, index = min((val, idx) for (idx, val) in enumerate(distances)) + closest_node_id = self.pathGoalsYawDegree[index][0] + + self.last_path_index = index + current_id = closest_node_id + self.current_id = current_id + + nodes_x_original = np.array([node[1] for node in self.pathGoalsYawDegreeOriginal]) + nodes_y_original = np.array([node[2] for node in self.pathGoalsYawDegreeOriginal]) + distances_original = np.sqrt((nodes_x_original - self.position_x)**2 + (nodes_y_original - self.position_y)**2) + min_distance_original, index_original = min((val, idx) for (idx, val) in enumerate(distances_original)) + closest_node_id_original = self.pathGoalsYawDegreeOriginal[index_original][0] + + self.current_id_original = closest_node_id_original + print("\n\n\n\ncurrent_id_original",self.current_id_original) + print("\n\n\n\n\ncurrent_id",self.current_id) + + else: + nodes_x = np.array([node[1] for node in self.pathGoalsYawDegreeCopy]) + nodes_y = np.array([node[2] for node in self.pathGoalsYawDegreeCopy]) + distances = np.sqrt((nodes_x - self.position_x)**2 + (nodes_y - self.position_y)**2) + min_distance, index = min((val, idx) for (idx, val) in enumerate(distances)) + closest_node_id = self.pathGoalsYawDegreeCopy[index][0] + + self.last_path_index = index + current_id = closest_node_id + self.current_id = current_id + + + sign_looking_band = [] + + nodes_x_for_obstacles = np.array([node[1] for node in self.pathGoalsYawDegreeCopy]) + nodes_y_for_obstacles = np.array([node[2] for node in self.pathGoalsYawDegreeCopy]) + + distances = np.sqrt((nodes_x_for_obstacles - self.position_x)**2 + (nodes_y_for_obstacles - self.position_y)**2) + min_distance, index = min((val, idx) for (idx, val) in enumerate(distances)) + current_id_for_obstacles = self.pathGoalsYawDegreeCopy[index][0] + print(type(current_id_for_obstacles)) + + for i in range(1, 5): + matching_pairs_signs = [pair for pair in self.SourceTargetNodesCopy if pair[0] == current_id_for_obstacles] + matching_entry = next((entry for entry in self.pathGoalsYawDegreeCopy if entry[0] == current_id_for_obstacles), None) + + print("testing") + print("matching_entry",matching_entry) + print("matcihing_pairs_signs",matching_pairs_signs) + if matching_pairs_signs: + next_id_signs = matching_pairs_signs[0][1] + + matching_entry_second = next((entry for entry in self.pathGoalsYawDegreeCopy if entry[0] == next_id_signs), None) + third_pairs_signs = [pair for pair in self.SourceTargetNodesCopy if pair[0] == next_id_signs] + if third_pairs_signs: + print("third_pair_sings", third_pairs_signs) + matching_entry_third = next((entry for entry in self.pathGoalsYawDegreeCopy if entry[0] == third_pairs_signs[0][1]), None) + print("matching_entry_second",matching_entry_second) + print("next_id",next_id_signs) + + sign_looking_band.append(current_id_for_obstacles) + current_id_for_obstacles = next_id_signs + + TargetPosition = matching_entry[1:3] + # TargetPositionNext = matching_entry_second[1:3] + + x_diff = abs(matching_entry[1] - matching_entry_second[1]) + y_diff = abs(matching_entry[2] - matching_entry_second[2]) + + x_threshold = 0.10 + y_threshold = 0.10 + + + for center_x, center_y in zip(self.center_x, self.center_y): + ObstaclePosition = [center_x, center_y] + + # if "parking" in self.yolo_data.object: + for node_id, (x, y) in self.obstacle_node_positions.items(): + distance = np.sqrt((center_x - x) ** 2 + (center_y - y) ** 2) + if distance <= 0.4 and node_id not in self.parking_spot_is_full: + self.parking_spot_is_full.append(node_id) + self.parking_nodes_id.remove(node_id) # Engelli node ID'yi park alanlarından çıkar + + if self.target_node in self.parking_spot_is_full: + self.target_node = self.parking_nodes_id[0] + self.process_and_publish_data(self.current_id_original,self.target_node) + + + # TODO: Baha burada kaldın + rospy.loginfo("parking_spot_is_full: %s", self.parking_spot_is_full) + rospy.loginfo("parking_nodes_id: %s", self.parking_nodes_id) + + + # Calculate the norm + obstaclediff = np.linalg.norm(np.array(ObstaclePosition) - np.array(TargetPosition)) + + is_within_x_threshold = min(matching_entry[1], matching_entry_second[1]) - x_threshold <= center_x <= max(matching_entry[1], matching_entry_second[1]) + x_threshold + is_within_y_threshold = min(matching_entry[2], matching_entry_second[2]) - y_threshold <= center_y <= max(matching_entry[2], matching_entry_second[2]) + y_threshold + print("obstaclediff",obstaclediff) + print("center_x",center_x) + print("center_y",center_y) + print("is_within_x_threshold",is_within_x_threshold) + print("is_within_y_threshold",is_within_y_threshold) + + if obstaclediff < 0.15 or (is_within_x_threshold and is_within_y_threshold) and matching_entry[0] not in self.dont_check_obstacles_here: + # if obstaclediff < 0.15: + + print("obstaclediffINSIDE",obstaclediff) + print("current_idObstacle",self.current_id) + print("matching_entryObstacle",matching_entry[0]) + + self.past_obs_dontuse = self.obs_dontuse.copy() + + # self.obstacles_array = [] ## bu 2 kod sornadan kaldırılacaktır. + # self.obs_dontuse=["489","127","96"]#kullanma listesi + + if matching_entry[0] not in self.obstacles_array: + self.obstacles_array.append(matching_entry[0]) + if third_pairs_signs: + if matching_entry_third[0] not in self.obstacles_array: + self.obstacles_array.append(matching_entry_third[0]) + if matching_entry_second[0] not in self.obstacles_array: + self.obstacles_array.append(matching_entry_second[0]) + + for obstacle_id in self.obstacles_array: + print("self.obs_dontuseinside",self.obs_dontuse) + print("self.obstacles_arrayinside",self.obstacles_array) + obstacle_id_str = str(obstacle_id) + if obstacle_id_str not in self.obs_dontuse: + self.obs_dontuse.insert(0, obstacle_id_str) + if self.obs_dontuse != self.past_obs_dontuse: + self.process_and_publish_data(self.current_id_original,self.target_node)#curent den global targete göndericem + self.last_update_time_obstacles_checking = rospy.Time.now() + + else: + print("obs_dontuse same") + + print("self.obs_dontuse",self.obs_dontuse) + print("self.obstacles_array",self.obstacles_array) + + if self.parking_spot_is_full: + self.parkings_are_available = list(set(self.parking_nodes_id) - set(self.parking_spot_is_full)) + print("self.parkings_are_available",self.parkings_are_available) + + if self.obstacles_array and self.flagsolla == 0: + for obstacle_id in self.obstacles_array: + # Code to execute for each obstacle + # ... + + matching_entry_obstacle_first = next((entry for entry in self.pathGoalsYawDegreeCopy if entry[0] == obstacle_id), None) + + matching_entry_id_second = [pair for pair in self.SourceTargetNodesCopy if pair[0] == obstacle_id] + matching_entry_obstacle_second = next((entry for entry in self.pathGoalsYawDegreeCopy if entry[0] == matching_entry_id_second[0][1]), None) + + targetposition_obstacle_first = matching_entry_obstacle_first[1:3] + targetposition_obstacle_second = matching_entry_obstacle_second[1:3] + # Calculate the norm + x_threshold = 0.15 + y_threshold = 0.15 + + is_within_x_threshold = min(matching_entry_obstacle_first[1], matching_entry_obstacle_second[1]) - x_threshold <= center_x <= max(matching_entry_obstacle_first[1], matching_entry_obstacle_second[1]) + x_threshold + is_within_y_threshold = min(matching_entry_obstacle_first[2], matching_entry_obstacle_second[2]) - y_threshold <= center_y <= max(matching_entry_obstacle_first[2], matching_entry_obstacle_second[2]) + y_threshold + + + if np.linalg.norm(np.array(targetposition_obstacle_first) - np.array(ObstaclePosition)) < 0.16 or np.linalg.norm(np.array(targetposition_obstacle_second) - np.array(ObstaclePosition)) < 0.16 or (is_within_x_threshold and is_within_y_threshold): + self.state = "waiting the obstacle move on" + self.last_update_time_obstacles_checking = rospy.Time.now() + self.obstaclesayac = 0 + + print("self.statefirst",self.state) + print("self.flagsolla",self.flagsolla) + self.obstaclesayac = self.obstaclesayac + 1 + + if self.state == "keep lane" and self.obstacles_array and self.flagsolla == 0 and self.obstaclesayac == 50: + + print("hi im here") + print("self.obstacles_array",self.obstacles_array) + for value in self.obstacles_array: + if value in self.obs_dontuse: + self.obs_dontuse.remove(value) + self.obstacles_array = [] + + self.process_and_publish_data(self.current_id_original,self.target_node)#curent den global targete göndericem + + # if any obstacle in the matching_pairs_signs create new path so call djikstra + + if any(sign_id in self.traffic_light_nodes_id for sign_id in sign_looking_band): + + rospy.loginfo("self.traffic_lights_nodes_idchecking") + + if self.traffic_light_master[0] == 0 and self.current_id =="489": + rospy.loginfo("self.traffic_light_master[0] == 0") + self.steerAngle = math.degrees(0.0) + self.steerLateral = float(0.0) + self.state = "waiting on traffic light" + self.last_update_time = rospy.Time.now() + # rospy.loginfo("self.state: {}".format(self.state)) + self.carControlPublisher() + + if self.traffic_light_slave[0] == 0 and self.current_id == "135": + rospy.loginfo("self.traffic_light_slave[0] == 0") + self.steerAngle = math.degrees(0.0) + self.steerLateral = float(0.0) + self.state = "waiting on traffic light" + self.last_update_time = rospy.Time.now() + + # rospy.loginfo("self.state: {}".format(self.state)) + self.carControlPublisher() + + if self.traffic_light_start[0] == 0 and self.current_id =="121": + rospy.loginfo("self.traffic_light_start[0] == 0") + self.steerAngle = math.degrees(0.0) + self.steerLateral = float(0.0) + self.state = "waiting on traffic light" + self.last_update_time = rospy.Time.now() + # rospy.loginfo("self.state: {}".format(self.state)) + self.carControlPublisher() + + elif any(sign_id in self.intercept_targets for sign_id in sign_looking_band) and "stop" in self.yolo_data.object: + + if current_id in self.intercept_targets: + + self.steerAngle = math.degrees(0.0) + self.steerLateral = float(0.0) + self.state = "waiting on intercept" + # rospy.loginfo("self.state: {}".format(self.state)) + self.carControlPublisher() + + else: + self.last_update_time = rospy.Time.now() + + elif any(sign_id in self.intercept_targets for sign_id in sign_looking_band) and "crosswalk" in self.yolo_data.object: + + if self.crosswalk_flag == False: + self.crosswalk_scenerio = True + # self.state = "no obstacle on crosswalk" + self.mpc_start_settings() + self.crosswalk_flag = True + self.crosswalk_update_time = rospy.Time.now() + + distance_meets_criteria = False + + rospy.loginfo("self.state: {}".format(self.state)) + + self.crosswalk_update_time = rospy.Time.now() + self.last_update_time = rospy.Time.now() + + else: + self.state = "keep lane" + rospy.loginfo("self.state: {}".format(self.state)) + + print("yolo_data.object",self.yolo_data.object) + if any(sign_id in self.intercept_targets for sign_id in sign_looking_band) and "parking" in self.yolo_data.object and self.parking_flag == False: + + self.park_scenerio = True + self.mpc_start_settings() + self.parking_flag = True + rospy.loginfo("self.state: park section id:1") + + if "motorway" in self.yolo_data.object and self.motorway_flag == False: + + self.motorway_scenerio = True + self.mpc_start_settings() + self.motorway_flag = True + + if "endofmotorway" in self.yolo_data.object and self.motorway_flag == False: + + self.motorway_scenerio = False + self.mpc_start_settings() + self.motorway_flag = False + + matching_pairs = [pair for pair in self.SourceTargetNodes if pair[0] == current_id] + print("matching_pairs",matching_pairs) + # print("pathGoalsYawDegree",self.pathGoalsYawDegree) + if not matching_pairs: + self.distance_flag = False + state_target_slice = self.state_target[:3] + + state_init_slice = self.state_init[:3] + + # Calculate the norm + distance = ca.norm_2(ca.DM(state_init_slice) - ca.DM(state_target_slice)) + if distance < 0.15: + self.state_target = ca.DM([self.position_x, self.position_y, self.yaw_rad]) + self.last_update_time = rospy.Time.now() + + if matching_pairs: + next_id = matching_pairs[0][1] + # print("next_id",next_id) + #print("self.pathGoalsYawDegree:",self.pathGoalsYawDegree) + #for i in range(1, 5): + + matching_entry = next((entry for entry in self.pathGoalsYawDegree if entry[0] == next_id), None) + print("matching_entry",matching_entry) + if matching_entry is not None: + target_x, target_y = matching_entry[1], matching_entry[2] + target_y = target_y + #print("target_x",target_x) + #print("target_y",target_y) + dx = target_x - self.position_x + dy = target_y - self.position_y + yaw = atan2(dy, dx) + # print("yaw",yaw) + + if pi < yaw: + changing_Value = yaw - pi + yaw = -pi + changing_Value + + if -pi > yaw: + changing_Value = yaw + pi + yaw = pi + changing_Value + + if pi < self.yaw_rad: + changing_Value = self.yaw_rad - pi + self.yaw_rad = -pi + changing_Value + + if -pi > self.yaw_rad: + changing_Value = self.yaw_rad + pi + self.yaw_rad = pi + changing_Value + + bandwidth = 1.57 + + if self.yaw_rad + bandwidth > pi and yaw < -1.57: + cars_value = pi - self.yaw_rad + goal_value = pi - abs(yaw) + yaw = cars_value + goal_value + self.yaw_rad + if self.yaw_rad - bandwidth < -pi and yaw > 1.57: + goal_value = pi - yaw + yaw = -pi - goal_value + + print(f'Step {1} -> ID: {next_id}, X: {target_x:.2f}, Y: {target_y:.2f}, Yaw: {yaw:.2f} rad') + self.last_time_went_node = rospy.Time.now() + + + if next_id in self.parking_nodes_id: + self.state_target = ca.DM([target_x, target_y, 0.0]) + self.distance_flag = False + else: + self.state_target = ca.DM([target_x, target_y, yaw]) + self.distance_flag = True + + + self.goal_id = matching_entry + if self.state == "keep lane": + self.last_update_time = rospy.Time.now() + + elif matching_entry is None: + self.distance_flag = False + self.state_target = ca.DM([self.position_x, self.position_y, self.yaw_rad]) + self.last_update_time = rospy.Time.now() + + if self.distance_flag == True: + + # if self.traffic_light_master[0] == 2 and self.current_id =="489": + # rospy.loginfo("self.traffic_light_master[0] == 0") + # self.steerAngle = math.degrees(0.0) + # self.steerLateral = float(0.0) + # self.state = "waiting on traffic light" + # # rospy.loginfo("self.state: {}".format(self.state)) + # self.carControlPublisher() + + # if self.traffic_light_slave[0] == 2 and self.current_id == "135": + # rospy.loginfo("self.traffic_light_slave[0] == 0") + # self.steerAngle = math.degrees(0.0) + # self.steerLateral = float(0.0) + # self.state = "waiting on traffic light" + # # rospy.loginfo("self.state: {}".format(self.state)) + # self.carControlPublisher() + + # if self.traffic_light_start[0] == 2 and self.current_id =="121": + # rospy.loginfo("self.traffic_light_start[0] == 0") + # self.steerAngle = math.degrees(0.0) + # self.steerLateral = float(0.0) + # self.state = "waiting on traffic light" + # # rospy.loginfo("self.state: {}".format(self.state)) + # self.carControlPublisher() + + + matching_pairs = [pair for pair in self.SourceTargetNodes if pair[0] == self.current_id] + next_id = matching_pairs[0][1] + matching_entry = next((entry for entry in self.pathGoalsYawDegree if entry[0] == next_id), None) + if matching_pairs: + target_x, target_y = matching_entry[1], matching_entry[2] + target_y = target_y + dx = target_x - self.position_x + dy = target_y - self.position_y + yaw = atan2(dy, dx) + + if pi < yaw: + changing_Value = yaw - pi + yaw = -pi + changing_Value + + elif -pi > yaw: + changing_Value = yaw + pi + yaw = pi + changing_Value + + if pi < self.yaw_rad: + changing_Value = self.yaw_rad - pi + self.yaw_rad = -pi + changing_Value + + elif -pi > self.yaw_rad: + changing_Value = self.yaw_rad + pi + self.yaw_rad = pi + changing_Value + + bandwidth = 1.57 + + if self.yaw_rad + bandwidth > pi and yaw < -1.57: + cars_value = pi - self.yaw_rad + goal_value = pi - abs(yaw) + yaw = cars_value + goal_value + self.yaw_rad + elif self.yaw_rad - bandwidth < -pi and yaw > 1.57: + goal_value = pi - yaw + yaw = -pi - goal_value + + self.state_target = ca.DM([target_x, target_y, yaw]) + + if self.debug: + self.debug_callback() + + # State Vector [x y yaw v s]' + + + except AttributeError as e: + print(e) + rospy.logerr(e) + + def yolo_intercept_callback(self,data): + rospy.loginfo("yolo_intercept_callback") + self.yolo_data = MekatronomYolo() + self.yolo_data.object = data.object + rospy.loginfo("self.yolo_data.object: {}".format(self.yolo_data.object)) + + + def behaviourTimerCallback(self,event): + rospy.loginfo("behaviourTimerCallback") + + if (rospy.Time.now() - self.last_time_went_node >= rospy.Duration(10.0)) and (rospy.Time.now() - self.last_update_time_obstacles_checking >= rospy.Duration(10.0)): + rospy.loginfo("behaviourTimerCallback Last Time Went Node and Last Update Time Obstacles Checking") + + self.last_time_went_node = rospy.Time.now() + self.process_and_publish_data(self.current_id_original,self.target_node) + + if self.mpc_started: + matching_pairs = [pair for pair in self.SourceTargetNodesCopy if pair[0] == self.goal_id[0]] + rospy.loginfo("behaviourtimercallback inside of it") + next_id = matching_pairs[0][1] + + matching_entry = next((entry for entry in self.pathGoalsYawDegreeCopy if entry[0] == next_id)) + self.goal_id = matching_entry + if matching_entry: + target_x, target_y = matching_entry[1], matching_entry[2] + target_y = target_y + + dx = target_x - self.position_x + dy = target_y - self.position_y + yaw = atan2(dy, dx) + + if pi < yaw: + changing_Value = yaw - pi + yaw = -pi + changing_Value + + elif -pi > yaw: + changing_Value = yaw + pi + yaw = pi + changing_Value + + if pi < self.yaw_rad: + changing_Value = self.yaw_rad - pi + self.yaw_rad = -pi + changing_Value + + elif -pi > self.yaw_rad: + changing_Value = self.yaw_rad + pi + self.yaw_rad = pi + changing_Value + + + bandwidth = 1.57 + + if self.yaw_rad + bandwidth > pi and yaw < -1.57: + cars_value = pi - self.yaw_rad + goal_value = pi - abs(yaw) + yaw = cars_value + goal_value + self.yaw_rad + elif self.yaw_rad - bandwidth < -pi and yaw > 1.57: + goal_value = pi - yaw + yaw = -pi - goal_value + + print(f'Step {1} -> ID: {next_id}, X: {target_x:.2f}, Y: {target_y:.2f}, Yaw: {yaw:.2f} rad') + + + self.state_target = ca.DM([target_x, target_y, yaw]) + self.carControlPublisher() + + if rospy.Time.now() - self.last_update_time_obstacles_checking >= rospy.Duration(10.0): + self.last_update_time_obstacles_checking = rospy.Time.now() + self.intercept_targets = ["243","261","252","54","51","56","317","367","397","404","34","84","163","80","93","176", + "91","14","18","2","6","143","102","32","16","222","219","30","38","100","26","42", + "109","113","121","75","185","71","40","205","226","283","198"] + + self.crosswalk_scenerio = False + self.crosswalk_flag = False + + + if self.obstacles_array: + print("hi im here behavior") + print("self.obstacles_array",self.obstacles_array) + for value in self.obstacles_array: + if value in self.obs_dontuse: + self.obs_dontuse.remove(value) + self.obstacles_array = [] + print("angel kalktı ") + # self.process_and_publish_data(self.current_id_original,self.target_node)#curent den global targete göndericem + self.last_update_time_obstacles_checking = rospy.Time.now() + + if rospy.Time.now() - self.crosswalk_update_time >= rospy.Duration(5.0) and self.crosswalk_flag == True: + self.crosswalk_update_time = rospy.Time.now() + self.crosswalk_flag = False + self.crosswalk_scenerio = False + self.mpc_start_settings() + + if rospy.Time.now() - self.last_update_time >= rospy.Duration(1.0): + + if self.state == "waiting on traffic light": + + if self.traffic_light_master[0] == 2 and self.current_id =="489": + self.state = "keep lane" + if self.traffic_light_slave[0] == 2 and self.current_id == "135": + self.state = "keep lane" + if self.traffic_light_start[0] == 2 and self.current_id =="121": + self.state = "keep lane" + + matching_pairs = [pair for pair in self.SourceTargetNodes if pair[0] == self.goal_id[0]] + + if matching_pairs and self.state == "keep lane": + next_id = matching_pairs[0][1] + + #print("self.pathGoalsYawDegree:",self.pathGoalsYawDegree) + #for i in range(1, 5): + + matching_entry = next((entry for entry in self.pathGoalsYawDegree if entry[0] == next_id)) + self.goal_id = matching_entry + if matching_entry: + target_x, target_y = matching_entry[1], matching_entry[2] + target_y = target_y + #print("target_x",target_x) + #print("target_y",target_y) + dx = target_x - self.position_x + dy = target_y - self.position_y + yaw = atan2(dy, dx) + + if pi < yaw: + changing_Value = yaw - pi + yaw = -pi + changing_Value + + elif -pi > yaw: + changing_Value = yaw + pi + yaw = pi + changing_Value + + if pi < self.yaw_rad: + changing_Value = self.yaw_rad - pi + self.yaw_rad = -pi + changing_Value + + elif -pi > self.yaw_rad: + changing_Value = self.yaw_rad + pi + self.yaw_rad = pi + changing_Value + + + bandwidth = 1.57 + + if self.yaw_rad + bandwidth > pi and yaw < -1.57: + cars_value = pi - self.yaw_rad + goal_value = pi - abs(yaw) + yaw = cars_value + goal_value + self.yaw_rad + elif self.yaw_rad - bandwidth < -pi and yaw > 1.57: + goal_value = pi - yaw + yaw = -pi - goal_value + + print(f'Step {1} -> ID: {next_id}, X: {target_x:.2f}, Y: {target_y:.2f}, Yaw: {yaw:.2f} rad') + + + self.state_target = ca.DM([target_x, target_y, yaw]) + + self.last_update_time = rospy.Time.now() + + if rospy.Time.now() - self.last_update_time >= rospy.Duration(5.0): + + + if self.state == "waiting on intercept": + + self.state = "keep lane" + self.intercept_targets.remove(self.current_id) # current_id'yi listeden kaldır + matching_pairs = [pair for pair in self.SourceTargetNodes if pair[0] == self.goal_id[0]] + + if matching_pairs: + next_id = matching_pairs[0][1] + + matching_entry = next((entry for entry in self.pathGoalsYawDegree if entry[0] == next_id)) + self.goal_id = matching_entry + if matching_entry: + target_x, target_y = matching_entry[1], matching_entry[2] + target_y = target_y + #print("target_x",target_x) + #print("target_y",target_y) + dx = target_x - self.position_x + dy = target_y - self.position_y + yaw = atan2(dy, dx) + + if pi < yaw: + changing_Value = yaw - pi + yaw = -pi + changing_Value + + elif -pi > yaw: + changing_Value = yaw + pi + yaw = pi + changing_Value + + if pi < self.yaw_rad: + changing_Value = self.yaw_rad - pi + self.yaw_rad = -pi + changing_Value + + elif -pi > self.yaw_rad: + changing_Value = self.yaw_rad + pi + self.yaw_rad = pi + changing_Value + + + bandwidth = 1.57 + + if self.yaw_rad + bandwidth > pi and yaw < -1.57: + cars_value = pi - self.yaw_rad + goal_value = pi - abs(yaw) + yaw = cars_value + goal_value + self.yaw_rad + elif self.yaw_rad - bandwidth < -pi and yaw > 1.57: + goal_value = pi - yaw + yaw = -pi - goal_value + + print(f'Step {1} -> ID: {next_id}, X: {target_x:.2f}, Y: {target_y:.2f}, Yaw: {yaw:.2f} rad') + + self.state_target = ca.DM([target_x, target_y, yaw]) + + self.last_update_time = rospy.Time.now() + + else: + pass + + + #OLD# new path finding + + ##################### NEW PATH FINDING ####################### + def process_and_publish_data(self,temp_source,temp_target): + # GraphML dosyasını okuma + print("burdayım :",self.current_id," orjinal noktam :",self.current_id_original) + + self.callnumber=self.callnumber+1 + stx = time.time()# dosyadan endge okuma işelmleri zaman ölçümü için + + tree = ET.parse(self.file_path_original) + root = tree.getroot() + self.nodes_data = self.extract_nodes_data(root) + + + self.obstacle_node_positions = {node_id: (x, y) for node_id, x, y in self.nodes_data if node_id in self.parking_nodes_id} + + + self.edges_data, self.edges_data_true_ilkverisyon = self.extract_edges_data(root) + + + flagsolla=self.flagsolla + for ciz in self.edges_data:#self.obs_dontuse listesindekilerden herhangi biri kesikli çizgi üstüne geliyo mu kontrolü + for kx in self.obs_dontuse: + if ciz[2]==True:#ciz[1]==kx and ciz[2]==True: yine benim aptallığım eğer flag sollaya daha once müdehale edilmemişse ve engel kalktığı senaryoda bu ife girmiycek kendi şeridine geri dönemiycek + flagsolla=1 + self.flagsolla = 1 + #print("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@solla") + break#fazladan dolaşmasın bi kere bulması yeterli + #print("################ : ",ciz[2]) + + if flagsolla==0: + self.flagsolla=0 + etx = time.time() + elapsed_timex = etx - stx + print('Execution time:', elapsed_timex, 'seconds##################-----------------####################') + + + if flagsolla==1: + #sollama kısmı için özel graph dosyasını açıcam ikinci bi edge çıkarma işlemi umarım fazla yavaşlatmaz + #şu anda 0.008 saniye + tree2 = ET.parse(self.graphml_file_path) + root2 = tree2.getroot() + #print("root : ",root2) + self.nodes_data = self.extract_nodes_data(root2) + self.edges_data, self.edges_data_true_ilkverisyon = self.extract_edges_data(root2) + + + + noded,edged=self.extract_graph()# (nodedict,edgedict) döndürür + + print("noded_first:",noded) + + #self.obs_dontuse=["360","314","321","344","315","270","367","64","169","326"] + + path_short=self.dijkstra(temp_source,temp_target,noded,edged,self.obs_dontuse)#nodedictionary =noded + print("pathshort:",path_short) + self.path_original = self.stformat(path_short) + print("noded_second:",noded) + self.nodedatabest=noded + print("pathoriginal:",self.path_original) + #print(noded) + + + newnodedictionary,stlist=self.beizer(path_short,noded)#beizere targete giden path i veriyorum + self.obs_dict = newnodedictionary#yeni node sozlüğü + self.edges_data_true =stlist#source target list + # print("self.obs_dict",self.obs_dict) + # print("self.edges_data_true",self.edges_data_true) + #print("-----------------------------------------") + + + #print("-----------------------------------------") + #nodedata ,sourcetarget + #beizer çıktısı 1 : source targetnodes tipinde path 2:yeni eklenen noktalarıda içeren node_data o datayıda self.obs_dicte vericem + #409 da döngüye kapılıyo çıkamıyo + #print("edges_data_true",self.edges_data_true) + #print("path_original",self.path_original) + + self.SourceTargetNodes = [(edge[0], edge[1]) for edge in self.edges_data_true if edge[2]] + self.SourceTargetNodesOriginal = [(edge[0], edge[1]) for edge in self.path_original if edge[2]] + #self.SourceTargetNodes edge formatında benim pathi edge formatına çevirmemlazım + #burda sourceın içine hedef yolu vermek için edges data ture formatında en kısa yolu ustte vermemlazım + #print("sorutceTrgetnodes:",self.edges_data_true) + #print("**************************************************") + #print("**************************************************") + + self.path = [] + self.pathOriginal = [] + + + for source_id, target_id in self.SourceTargetNodesOriginal: + if source_id in self.obs_dict: + source_coords = self.obs_dict[source_id] + self.pathOriginal.append((source_id, source_coords[0], source_coords[1])) + if target_id in self.obs_dict: + coords = self.obs_dict[target_id] + self.pathOriginal.append((target_id, coords[0], coords[1])) + # if source_id in self.obs_dict: + # coords = self.obs_dict[source_id] + # self.path.append((source_id, coords[0], coords[1])) + + # print("\nself.path_original::::\n",self.pathOriginal) + + for source_id, target_id in self.SourceTargetNodes: + if target_id in self.obs_dict: + coords = self.obs_dict[target_id] + self.path.append((target_id, coords[0], coords[1])) + + + # Her bir nokta için bir sonraki nokta ile arasındaki açıyı hesaplama + angles = [] + anglesOriginal = [] + + for i in range(len(self.pathOriginal) - 1): + dx = self.pathOriginal[i + 1][1] - self.pathOriginal[i][1] + # print("self.pathOriginal[i + 1][1]",self.pathOriginal[i + 1][1]) + # print("self.pathOriginal[i][1]",self.pathOriginal[i][1]) + dy = self.pathOriginal[i + 1][2] - self.pathOriginal[i][2] + + angle = math.atan2(dy, dx) + anglesOriginal.append(angle) + + # print("anglesOriginal",anglesOriginal) + + for i in range(len(self.path) - 1): + dx = self.path[i + 1][1] - self.path[i][1] + dy = self.path[i + 1][2] - self.path[i][2] + angle = math.atan2(dy, dx) + angles.append(angle) + + # print("angles",angles) + + if angles: # Check if angles list is not empty + angles.append(angles[-1]) + + if anglesOriginal: + anglesOriginal.append(anglesOriginal[-1]) + + self.pathGoalsYawDegree = [(*p, angle) for p, angle in zip(self.path, angles)] + self.pathGoalsYawDegreeOriginal = [(*p, angle) for p, angle in zip(self.pathOriginal, anglesOriginal)] + + # print("\nself.pathGoalsYawDegree\n",self.pathGoalsYawDegree) + # print("\nself.pathGoalsYawDegreeOriginal\n",self.pathGoalsYawDegreeOriginal) + + if not self.pathGoalsYawDegreecalled: + self.pathGoalsYawDegreeCopy = self.pathGoalsYawDegreeOriginal + self.SourceTargetNodesCopy = self.SourceTargetNodesOriginal + self.pathGoalsYawDegreecalled = True + + # Data publishing simulation + data_message = str(self.edges_data_true) + + + + def stformat(self,new_path): + source_target=[] + pathlen=len(new_path) + #print("len :",pathlen) + for n_edge in range(pathlen-1): + source_target.append((new_path[n_edge],new_path[n_edge+1],True)) + return source_target + + + def extract_nodes_data(self, root): + nodes_data = [] + for node in root.findall(".//{http://graphml.graphdrawing.org/xmlns}node"): + node_id = node.get('id') + d0 = None + d1 = None + for data in node: + if data.get('key') == 'd0': + d0 = float(data.text) # 'x' koordinatı + elif data.get('key') == 'd1': + d1 = float(data.text) # 'y' koordinatı + # past version is = d1 = 13.72-float(data.text) # 'y' koordinatı + + if d0 is not None and d1 is not None: + nodes_data.append((node_id, d0, d1)) + return nodes_data + + def extract_graph(self): + #print(self.edges_data) + # ('source','target',mesafe) + inf = sys.maxsize + graph_temp=self.edges_data + + nodedict=dict([]) + edgedict=dict([])#keyler source valuelerde (target,mesafe) + for node in self.nodes_data: + sollacurrent=False + for ciz in self.edges_data:#self.obs_dontuse listesindekilerden herhangi biri kesikli çizgi üstüne geliyo mu kontrolü + if ciz[0]==node[0]: + sollacurrent=ciz[2] + break#fazladan dolaşmasın bi kere bulması yeterli + + nodedict[node[0]]={'mesafe':inf,'atalist':[],'solla':sollacurrent,'x':node[1],'y':node[2]}#0 1 di 1 2 olarak düzelttim + + for edge in graph_temp: + + temp=edge[0] + edgemesafe =1#edge 0 ve edge 1 i verip ikisi arası mesafeyi vericem + if temp in edgedict.keys(): + + edgedict[edge[0]].append([edge[1],1]) + + else: + edgedict[edge[0]]=[[edge[1],1]] + #tuple dan list tipine cevirdim + + + edgetemp =edgedict[edge[0]] + + return (nodedict,edgedict) + + def minyol(self,edge_curent_source): + # print(edge_curent_source) + #print(edge_curent_source[0]) + #şimdilik her edge ağırlığı 1 diye önce 1. yi seçicek ama sonradan mesafe hesapolı edgelerde bu kısmı lazım + min=edge_curent_source[0][1] + min_id=edge_curent_source[0][0] + for edge in edge_curent_source: + if edge[1]2 : + #TODO: burası eskiden -2 idi -1yapmayı deniyoruz -2 de çalışıyor ama 2 node öncesinje veriyor irdelenecek. + befbagendxx=self.expath[indexnoEXxx-1]#sourn burda olabillir tek nokta kalan path de indexi bir geriye alıp patlıyor olabilir + else : + befbagendxx=self.expath[indexnoEXxx]#bunu test et + # print("before bagend",befbagendxx) + # print("bagend :::",bagendxx) + nodedictt[befbagendxx]['atalist']=nodedictt[befbagendxx]['atalist']+[befbagendxx] + return nodedictt[befbagendxx]['atalist'] + + else:#trafik ışığı kırmız değilse yaya yoksa ve bi onceki yol düzgün değilse çıkartılan yolu versin yani algoritmanın orijinal hali ile tepki versin + #orijinal hali + + self.yolvar=True + self.expath=nodedictt[target]['atalist'] + + return nodedictt[target]['atalist'] + else: + + newtargetn="" + + for tempstopx in yasaklistesi: + if tempstopx in self.expath: + bagend=tempstopx + break + + indexnoEX=self.expath.index(bagend) + # print("ex path:",self.expath) + if len(self.expath)>2 : + #TODO: burası eskiden -2 idi -1yapmayı deniyoruz -2 de çalışıyor ama 2 node öncesinje veriyor irdelenecek. + befbagend=self.expath[indexnoEX-2]#sourn burda olabillir tek nokta kalan path de indexi bir geriye alıp patlıyor olabilir + else : + befbagend=self.expath[indexnoEX]#bunu test et + + nodedictt[befbagend]['atalist']=nodedictt[befbagend]['atalist']+[befbagend] + return nodedictt[befbagend]['atalist'] + + + return nodedictt[target]['atalist'] + + def plotbfmc(self,node_dat,path): + #dosya adı için + obj = time.localtime(1627987508.6496193) + gren=[0,255,0] + red=[255,255,255] + orginx=0 + orginy=3863 + time_str = time.asctime(obj) + name='/home/mekatronom/out/'+time_str + #print("data:",node_dat) + #print("path:",path) + img=cv2.imread('/home/mekatronom/Downloads/Competition_track_graph.png') + #cv2.circle(img, (0,3863), 20, gren, -1) + for idxy in path: + x=int(orginx+node_dat[idxy][0]*282) + y=int(orginy-node_dat[idxy][1]*282) + #print("x:",x,type(x)) + #print("y:",y,type(y)) + if (int(idxy)-600)>0: + cv2.circle(img, (x,y), 15, red, -1) + else: + cv2.circle(img, (x,y), 15, gren, -1) + + + + #cv2.circle(img, (400,800), 15, gren, -1) + + #cv2.imshow('img', img) + cv2.imwrite(name+".png", img) + cv2.waitKey(1) + + def beizer(self,path,node_d): + new_node_data={} + for idkey in node_d.keys(): + #print(idkey) + new_node_data.update({idkey: (node_d[idkey]["x"],node_d[idkey]["y"])}) + #print("x:",x) + #print("new_node_data:",new_node_data) + new_point_counter=600#yeni düğmleri çakışma olmasın diye 600 den başlattım + new_path=path.copy()#path in kopyasını aldım değişiklikleri ona uyguluycam itarete ederken list den eleman silip ekliyince hata veriyo + + #print("----------------------") + path_length=len(path) + ppbuk=0 + + for f in range(path_length-2):#2 idi daha smooth dönüş için 3 deniyorum ben şükrü + + #açı hesaplıycam + if node_d[path[f]]["x"]==node_d[path[f+1]]["x"]: + #sıfıra bölme durumu + angel_rad1=1.57#radyan cinsinden 90derece + else: + angel_rad1=math.atan((node_d[path[f]]["y"]-node_d[path[f+1]]["y"])/(node_d[path[f]]["x"]-node_d[path[f+1]]["x"]))#arctan + + angel_deg1=angel_rad1*57.3 + #print("açı bir :",angel_deg1) + + if node_d[path[f+1]]["x"]==node_d[path[f+2]]["x"]: + #sıfıra bölme durumu + angel_rad2=1.57#radyan cinsinden 90derece + else: + angel_rad2=math.atan((node_d[path[f+1]]["y"]-node_d[path[f+2]]["y"])/(node_d[path[f+1]]["x"]-node_d[path[f+2]]["x"]))#arctan + + angel_deg2=angel_rad2*57.3 + #print("açı iki :",angel_deg2) + + b_andgel=abs(angel_deg1-angel_deg2) + #print("birinci açı :",angel_deg1," ikinci açı :",angel_deg2) + numout=1 + + if b_andgel>55 and b_andgel<110 :#b_andgel>45 and b_andgel<110 and (ppbuk+1)!=f ppbuk sorunlu düzgün çalışmıyo + ppbuk=f + if True or not (node_d[path[f]]["solla"] or node_d[path[f+1]]["solla"] or node_d[path[f+2]]["solla"]) :#if not self.flagsolla : bu eski hali yeni hali olarak solla ile kontrol etsem burayı çok iyi olur + #bezier ilk versiyon orijinal + + numPts=2 + numout=2 + + + controlPts=[[node_d[path[f]]["x"],node_d[path[f]]["y"]],[node_d[path[f+1]]["x"],node_d[path[f+1]]["y"]],[node_d[path[f+2]]["x"],node_d[path[f+2]]["y"]]]# control points + t=np.array([i*1/numPts for i in range(0,numPts+1)]) + + B_x=(1-t)*((1-t)*controlPts[0][0]+t*controlPts[1][0])+t*((1-t)*controlPts[1][0]+t*controlPts[2][0])#yeni noktaların x i + B_y=(1-t)*((1-t)*controlPts[0][1]+t*controlPts[1][1])+t*((1-t)*controlPts[1][1]+t*controlPts[2][1])#yeni y si + temp_new_nodelist=[] + for new_p in range(1,numPts):#+1 yapıp bütün noktaları yazdırdım yeni düğüm eklerken 1 den numPts ye kadar gezip yeni düğüm ekliycem + + + self.new_point_ctr=self.new_point_ctr+1 + new_point_str=str(self.new_point_ctr) + #print("yeni nokta ",new_p,"x:",B_x[new_p],"y:",B_y[new_p]) + new_node_data.update({new_point_str: (B_x[new_p],B_y[new_p])}) + if self.nodedatabest[path[f]]["solla"] : + self.nodedatabest[new_point_str] = {'mesafe': float('inf'), 'atalist': [], 'solla': True, 'x': B_x[new_p], 'y': B_y[new_p]} + #self.nodedatabest.update({new_point_str: (99999,[],True,B_x[new_p],B_y[new_p])})#nezihe sor + temp_new_nodelist.append(new_point_str)#nodu temp liste ekliyorum + new_path.remove(path[f+1])#üst if + + + temp_index=path.index(path[f+1]) + + for insrt in temp_new_nodelist: + new_path.insert(temp_index,insrt) + temp_index=temp_index+1 + new_new_path=new_path.copy() + pathlen22= len(new_path) + print("::::::::::::::::::::::",new_new_path) + for djj in range(pathlen22-3): + #print("şşş:",new_new_path[djj]," ",int(new_new_path[djj])/100==6) + if int(new_new_path[djj])> 600 and int(new_new_path[djj])< 800: + # nodedict[node[0]]={'mesafe':inf,'atalist':[],'solla':sollacurrent,'x':node[1],'y':node[2]} + if new_new_path[djj-1] in self.nodedatabest.keys() or new_new_path[djj+1] in self.nodedatabest.keys() : + + print(self.nodedatabest[new_new_path[djj-1]]["solla"] ," ",self.nodedatabest[new_new_path[djj+1]]["solla"]) + if self.nodedatabest[new_new_path[djj-1]]["solla"] or self.nodedatabest[new_new_path[djj+1]]["solla"]: + new_new_path.remove(new_new_path[djj])#break ver belki çalışır + + + new_path=new_new_path + print("new path :",new_new_path)#en son path + source_target=[] + pathlen=len(new_new_path) + #print("len :",pathlen) + for n_edge in range(pathlen-1): + source_target.append((new_new_path[n_edge],new_new_path[n_edge+1],True)) + + return new_node_data,source_target + + + + def extract_edges_data(self, root): + edges_data = [] + edges_data_true = [] + for edge in root.findall(".//{http://graphml.graphdrawing.org/xmlns}edge"): + source_id = edge.get('source') + target_id = edge.get('target') + data_d2 = edge.find(".//{http://graphml.graphdrawing.org/xmlns}data[@key='d2']") + d2_value = data_d2 is not None and data_d2.text == 'True' + edges_data.append((source_id, target_id, d2_value)) + #edeges_data normal dutun edgelerin verisi edges_data_true ranges dahilindeki true olan edgeler + return edges_data, edges_data_true + +############################################newpathfinding + + def obstacleDetector_callback(self, data): + # print("obstacleDetector_callback") + self.center_x = [] + self.center_y = [] + if not data.circles: # Eğer circles dizisi boşsa + # Direkt olarak 0.0, 0.0 ekleyin + # self.center_x.append(0.0) + # self.center_y.append(0.0) + pass + else: + # Eğer circles boş değilse, her bir dairenin merkez koordinatlarını al ve listeye ekle + for circle in data.circles: + self.center_x.append(circle.center.x) + self.center_y.append(circle.center.y) + + def carControlPublisher(self): + carData = {} + carData = { + 'action': '2', + 'steerAngle': self.steerAngle + # 'steerAngle': 0.0 + } + # print("data",carData) + self.carData = json.dumps(carData) + self.carControl.publish(self.carData) + #rospy.sleep(0.01) + car2Data = {} + # İkinci mesaj için veriler + car2Data = { + 'action': '1', + 'speed': self.steerLateral + # 'speed': 0.0 + } + self.car2Data = json.dumps(car2Data) + self.carControl.publish(self.car2Data) + # print("self.steerAngle",self.steerAngle) + # print("self.steerLateral",self.steerLateral) + + def localisation_callback(self, data): + # rospy.loginfo("Received localisation data - Timestamp: %s, posA: %f, posB: %f, rotA: %f, rotB: %f", + # data.timestamp, data.posA , 13.8-data.posB , data.rotA, data.rotB) + + self.position_x = data.pose.pose.position.x + self.position_y = data.pose.pose.position.y + + localisation_yaw_rad = tf.transformations.euler_from_quaternion([data.pose.pose.orientation.x, data.pose.pose.orientation.y, data.pose.pose.orientation.z, data.pose.pose.orientation.w])[2] + + # rospy.loginfo("Received IMU data yawlocalisation: %f", localisation_yaw_rad) + # #rospy.loginfo(f'Received localisation data - Timestamp: {data.timestamp}, posA: {data.posA}, posB: {data.posB}, rotA: {data.rotA}, rotB: {data.rotB}') + self.localisation_CB = True + + + def imu_callback(self, data): + + self.yaw_rad = tf.transformations.euler_from_quaternion([data.orientation.x, data.orientation.y, data.orientation.z, data.orientation.w])[2] + + # rospy.loginfo("Received IMU data yaw: %f", self.yaw_rad) + self.IMU_cb = True + + + #Callbacks Finish + + def image_callback(self, data): + try: + + if self.localisation_CB == True and self.IMU_cb == True: + + # if self.debug: + # self.debug_callback() + pass + + except AttributeError as e: + #print(e) + rospy.logerr(e) + + def traffic_light_master_callback(self, data): + # print("traffic_light_master_callback") + position_x = 4.21 + position_y = 3.30 + self.traffic_light_master = [data.data, position_x, position_y] + + def traffic_light_slave_callback(self, data): + # print("traffic_light_slave_callback") + position_x = 2.23 + position_y = 4.54 + self.traffic_light_slave = [data.data, position_x, position_y] + + def traffic_light_start_callback(self, data): + + position_x = 5.63 + position_y = 4.30 + self.traffic_light_start = [data.data, position_x, position_y] + + + #Debug Start + def debug_callback(self): + current_time = rospy.Time.now().to_sec() + PassedTime = current_time - self.prev_time + # rospy.loginfo("Elapsed time: %.4f seconds", PassedTime) + self.prev_time = current_time + + + + #Debug Finish + + def shutdown_hook(self): + # This function will be called during the shutdown process. + print("Shutting down, sending stop commands...") + stop_data = json.dumps({ + 'action': '2', + 'steerAngle': 0.0, + }) + self.carControl.publish(stop_data) + + stop_data2 = json.dumps({ + 'action': '1', + 'speed': 0.0, + }) + self.carControl.publish(stop_data2) + rospy.sleep(0.5) # Give some time for the message to be sent + +def main(): + rospy.init_node('mpc', anonymous=False) + mpc = MPC() + try: + while not rospy.is_shutdown(): + rospy.spin() + except KeyboardInterrupt: + print("Shutting down") + cv2.destroyAllWindows() + finally: + rospy.signal_shutdown('Manual shutdown') # Ensure rospy shutdown hooks are called + + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/src/example/src/pathfinding.py b/src/example/src/pathfinding.py new file mode 100644 index 0000000..55dd053 --- /dev/null +++ b/src/example/src/pathfinding.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python +# coding: utf-8 + +import rospy +from std_msgs.msg import String +import xml.etree.ElementTree as ET +import pandas as pd + +class GraphDataPublisher: + def __init__(self): + rospy.init_node('graph_data_publisher') + + # Parametreleri al + self.file_path = rospy.get_param('~file_path', '/home/baha/kodcalismalari/boschmap/Competition_track_graph.graphml') + + # Publisher + self.data_pub = rospy.Publisher('~graph_data', String, queue_size=10) + + # Veri işleme ve yayınlama + self.process_and_publish_data() + + def process_and_publish_data(self): + # GraphML dosyasını okuma + tree = ET.parse(self.file_path) + root = tree.getroot() + + # Düğüm verilerini çıkarma + nodes_data = self.extract_nodes_data(root) + + # Kenar verilerini çıkarma + edges_data, edges_data_true = self.extract_edges_data(root) + + # Verileri yayınlamak için hazırla + data_message = String() + data_message.data = str(edges_data_true) # Örnek olarak edges_data_true kullanıldı + self.data_pub.publish(data_message) + + rospy.loginfo("Graph data published") + + def extract_nodes_data(self, root): + nodes_data = [] + for node in root.findall(".//{http://graphml.graphdrawing.org/xmlns}node"): + node_id = node.get('id') + d0 = None + d1 = None + for data in node: + if data.get('key') == 'd0': + d0 = float(data.text) # 'x' koordinatı + elif data.get('key') == 'd1': + d1 = float(data.text) # 'y' koordinatı + if d0 is not None and d1 is not None: + nodes_data.append((node_id, d0, d1)) + return nodes_data + + def extract_edges_data(self, root): + edges_data = [] + edges_data_true = [] + for edge in root.findall(".//{http://graphml.graphdrawing.org/xmlns}edge"): + source_id = edge.get('source') + target_id = edge.get('target') + data_d2 = edge.find(".//{http://graphml.graphdrawing.org/xmlns}data[@key='d2']") + d2_value = data_d2 is not None and data_d2.text == 'True' + source_id_int = int(source_id) + target_id_int = int(target_id) + ranges = [(469, 479), (409, 425), (386, 398), (357, 365), (259, 281)] + if any(start <= source_id_int <= end for start, end in ranges) and any(start <= target_id_int <= end for start, end in ranges): + d2_value = True + edges_data_true.append((source_id, target_id, d2_value)) + edges_data.append((source_id, target_id, d2_value)) + return edges_data, edges_data_true + +if __name__ == '__main__': + try: + graph_data_publisher = GraphDataPublisher() + rospy.spin() + except rospy.ROSInterruptException: + pass diff --git a/src/example/src/pointcloudrobotaksi.py b/src/example/src/pointcloudrobotaksi.py new file mode 100755 index 0000000..20a86a1 --- /dev/null +++ b/src/example/src/pointcloudrobotaksi.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python + +import rospy +from sensor_msgs.msg import PointCloud2 + +def callback(data): + data.header.frame_id = 'automobile_chassis_link' + pub.publish(data) + +if __name__ == '__main__': + rospy.init_node('change_frame_id', anonymous=True) + rospy.Subscriber('/cloud_pcd', PointCloud2, callback) + pub = rospy.Publisher('/cloud_pcd_transformed', PointCloud2, queue_size=10) + rospy.spin() diff --git a/src/example/src/real_camera.py b/src/example/src/real_camera.py new file mode 100755 index 0000000..a36d6cc --- /dev/null +++ b/src/example/src/real_camera.py @@ -0,0 +1,782 @@ +#!/usr/bin/env python3 + +# Copyright (c) 2019, Bosch Engineering Center Cluj and BFMC organizers +# All rights reserved. + +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: + +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. + +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. + +# 3. Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. + +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE + +import rospy +import cv2 +import numpy as np +import torch +from sensor_msgs.msg import Image +from cv_bridge import CvBridge, CvBridgeError +import math +from geometry_msgs.msg import Twist +from std_msgs.msg import String +import json +from geometry_msgs.msg import PoseStamped +import serial +import time +#from ultralytics import YOLO + + +class realCamera(): + # ===================================== INIT========================================== + def __init__(self,camera_topic_name,pose_topic_name): + """ + Creates a bridge for converting the image from Gazebo image intro OpenCv image + """ + + #bölgelere ilk değeri 0 veriyorum. baslangic + self.on = 0 + self.onsag = 0 + self.onsol = 0 + self.sag = 0 + self.sol = 0 + self.onsolengel = 0 + self.onsagengel = 0 + self.speed = 0.2 + self.sure = 0 + self.derece_verisi_counter = 0 + self.engel = 0 + self.orta_cizgi_yakinlik = 0 + self.serit_degisti = 0 + self.carside_cb = True # Normalde false simdilik True + self.serit_kontrol = 0 + self.konum = 0 + self.arac_eylem = "yol_temiz" + self.yol_kapali = 0 + self.lane_type = "sheesh" + self.eylem = "yol_temiz" + + self.degree_veri = 0 + #self.ser =serial.Serial(baudrate=115200,port='/dev/ttyACM0') + #bölge ayarlaması bitti + + #arac boyut tanımlamaları: + self.vehicleWidth = 0.6 + self.vehicleLength = 0.28 + + # isik tanımlamaları + self.kirmizi_isik = 0 + self.yesil_isik = 0 + self.durak_giriliyor = 0 + + self.iterasyon_degeri=0 + self.yaw_degree = 0 + self.mpc_iter = 0 + + # + self.y_offset = 0 + self.x_offset = 0 + self.depth_x = 0 + self.depth_y = 0 + self.depth_cb_done = False + self.steering_angle = 0 + self.mavi_cizgi_sol_üst = 0 + + self.theta = 0 + + self.not_shifted_array = [] + self.shifted_array = [] + self.shifted_array_aviable = False + self.odometry_cb = False + self.hedef_x = 0 + + self.twist = Twist() # Gazeboda arabayi sürmek için + self.msg = String() + + self.bridge = CvBridge() + self.cv_image = np.zeros((640, 480)) + self.rgb=rospy.Subscriber(camera_topic_name,Image,self.image_callback) + self.pose=rospy.Subscriber(pose_topic_name,PoseStamped,self.pose_callback) + print("realCamera node") + self.device = 'cuda' if torch.cuda.is_available() else 'cpu' + print(f"Device: {self.device}") + + + def image_callback(self, data): + + try: + start_time = time.time() + img = self.bridge.imgmsg_to_cv2(data, "bgr8") + gray_image = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) + img_width = 640 + img_height = 480 + #img = cv2.resize(img, (img_width, img_height)) + cv2.imshow('color', img) + # cv2.waitKey(3) + # cv2.imwrite('imgContourPers.jpg', img) + + # c_thresh1 = 0 + # c_thresh2 = 70 + hue_min = 0 + hue_max = 60 + sat_min = 0 + sat_max = 255 + val_min = 70 + val_max = 255 + + # Görüntüyü HSV renk uzayına dönüştürün + # hsv_img = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) + + # HSV değerlerine dayalı maske oluşturun + lower_bound = np.array([hue_min, sat_min, val_min]) + upper_bound = np.array([hue_max, sat_max, val_max]) + # mask = cv2.inRange(hsv_img, lower_bound, upper_bound) + + imgBlur= cv2.GaussianBlur(img, (7, 7), 1) + + imgHSV= cv2.cvtColor(imgBlur, cv2.COLOR_BGR2HSV) + + imgMask = cv2.inRange(gray_image, 200, 255) + #cv2.imshow('mask', imgMask) + + kernel = np.ones((5, 5)) + imgDil = cv2.dilate(imgMask, kernel, iterations=2) + + imgContour = imgDil.copy() + + contours, hierarchy = cv2.findContours(imgDil, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE) + + cv2.drawContours(imgContour, contours, -1, (255, 0, 255), 3) + + # cv2.imshow("imgcounterLeft", imgContourLeft) + # cv2.imshow("imgcounterRight", imgContourRight) + + # for cntLeft in contoursLeft: + # areaLeft = cv2.contourArea(cntLeft) + # if areaLeft > 1000: # DÜZENLENEBİLİR - Burada sadece alanı 1000 birim + # periLeft = cv2.arcLength(cntLeft, True) + # approxLeft = cv2.approxPolyDP(cntLeft, 0.02 * periLeft, True) + # xLeft, yLeft, wLeft, hLeft = cv2.boundingRect(approxLeft) + # aLeft = int(wLeft / 2 + xLeft) + # bLeft = int(hLeft / 2 + yLeft) + # cv2.rectangle(imgContourLeft, (xLeft, yLeft), (xLeft + wLeft, yLeft + hLeft), (0, 255, 0), 5) + # cv2.rectangle(imgContourLeft, (aLeft, yLeft), (aLeft, yLeft + hLeft), (255, 0, 0), 2) + # cv2.line(imgContourLeft, (aLeft, bLeft), (640, 27), (255, 255, 0), 1) + # # distanceLeft = int(640 - aLeft) + # # solSeritDurum = "OK" + # # else: solSeritDurum = "KAYIP" + + width, height = 640, 480 + + bottom_right = [990, 480] # 640, 480 + bottom_left = [-350, 480] # 0, 480 + top_right = [600, 240] + top_left = [40, 240] + + src = np.float32([top_left, top_right, bottom_left, bottom_right]) + + # Dönüşüm sonucu alınacak köşe noktalarını belirleme + dst = np.float32([[0, 0], [width, 0], [0, height], [width, height]]) + matrix = cv2.getPerspectiveTransform(src, dst) + imgWarp = cv2.warpPerspective(img, matrix, (width, height)) + + imgContourPers = imgWarp.copy() + gray_image = cv2.cvtColor(imgContourPers, cv2.COLOR_BGR2GRAY) + imgMaskGray = cv2.inRange(gray_image, 230, 255) + imgHSVPers = cv2.cvtColor(imgWarp, cv2.COLOR_BGR2HSV) + imgMaskPers = cv2.inRange(imgHSVPers, lower_bound, upper_bound) + + imgDilPers = cv2.dilate(imgMaskGray, kernel, iterations=2) + contoursPers, hierarchyPers = cv2.findContours(imgDilPers, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE) + cv2.drawContours(imgContourPers, contoursPers, -1, (255, 0, 255), 1) + + + # 640 x 360 için + pixel_val = np.arange(25, (img_height - 25), 25) + + # 1280 x 720 için + # pixel_val = np.arange(25, (img_height - 25), 25) + def calculate_steering_angle_new(L, R, T): + #L arac uzunlugu + #R curve yaricapi + #T tekerler arasi mesafe + + steering_angle_in = math.atan(L/(R-(0.5*T))) + steering_angle_out = math.atan(L/(R+(0.5*T))) + + print(f"Steering angle in {math.degrees(steering_angle_in)}") + print(f"Steering angle out {steering_angle_out}\n") + + return steering_angle_in, steering_angle_out + + def calculate_steering_angle_new_oldstyle(lx, rx, ly, ry): + mid_y_left = sum(ly)/len(ly) + mid_x_left = sum(lx)/len(lx) + + mid_y_right = sum(ry) / len(ry) + mid_x_right = sum(rx) / len(rx) + + mid_steering_y = (mid_y_left + mid_y_right) / 2 + mid_steering_x = (mid_x_left + mid_x_right) / 2 + steering_point = [mid_steering_x, mid_steering_y] + print(f"Steering Point {steering_point}") + bottom_mid = [320, 480] + # cv2.line(merged_img, steering_point, bottom_mid, color=(0, 255, 0), thickness=2) + steering_angle = math.atan((steering_point[0] - bottom_mid[0]) / (steering_point[1] - bottom_mid[1])) + steering_angle = 90 - (90 - math.degrees(steering_angle)) + self.steering_angle = steering_angle + green_text = "\033[92m" # green + reset_text = "\033[0m" # reset to default color + #print(f"{green_text}Steering Angle {self.steering_angle}{reset_text}") + # 180 - (90 - math.degrees(steering_angle)) + + steering_angle_limit = 20 + if self.steering_angle > steering_angle_limit: + self.steering_angle = steering_angle_limit + elif self.steering_angle < -steering_angle_limit: + self.steering_angle = -steering_angle_limit + + #print(f"Steering angle old style {self.steering_angle}") + + return steering_point + + def get_poly_points(left_fit, right_fit): + ''' + Get the points for the left lane/ right lane defined by the polynomial coeff's 'left_fit' + and 'right_fit' + :param left_fit (ndarray): Coefficients for the polynomial that defines the left lane line + :param right_fit (ndarray): Coefficients for the polynomial that defines the right lane line + : return (Tuple(ndarray, ndarray, ndarray, ndarray)): x-y coordinates for the left and right lane lines + ''' + ysize, xsize = 480, 640 + + # Get the points for the entire height of the image + plot_y = np.linspace(0, ysize - 1, ysize) + plot_xleft = left_fit[0] * plot_y ** 2 + left_fit[1] * plot_y + left_fit[2] + plot_xright = right_fit[0] * plot_y ** 2 + right_fit[1] * plot_y + right_fit[2] + + # But keep only those points that lie within the image + plot_xleft = plot_xleft[(plot_xleft >= 0) & (plot_xleft <= xsize - 1)] + plot_xright = plot_xright[(plot_xright >= 0) & (plot_xright <= xsize - 1)] + plot_yleft = np.linspace(ysize - len(plot_xleft), ysize - 1, len(plot_xleft)) + plot_yright = np.linspace(ysize - len(plot_xright), ysize - 1, len(plot_xright)) + + return plot_xleft.astype(int), plot_yleft.astype(int), plot_xright.astype(int), plot_yright.astype( + int) + + def compute_offset_from_center(poly_param, x_mppx): + ''' + Computes the offset of the car from the center of the detected lane lines + :param poly_param (ndarray): Set of 2nd order polynomial coefficients that represent the detected lane lines + :param x_mppx (float32): metres/pixel in the x-direction + :return (float32): Offset + ''' + plot_xleft, plot_yleft, plot_xright, plot_yright = get_poly_points(poly_param[0], poly_param[1]) + + lane_center = (plot_xright[-1] + plot_xleft[-1]) / 2 + car_center = 640 / 2 + + offset = (lane_center - car_center) * x_mppx + return offset + + def compute_curvature(poly_param, y_mppx, x_mppx): + ''' + Computes the curvature of the lane lines (in metres) + :param poly_param (ndarray): Set of 2nd order polynomial coefficients that represent the detected lane lines + :param y_mppx (float32): metres/pixel in the y-direction + :param x_mppx (float32): metres/pixel in the x-direction + :return (float32): Curvature (in metres) + ''' + plot_xleft, plot_yleft, plot_xright, plot_yright = get_poly_points(poly_param[0], poly_param[1]) + + y_eval = np.max(plot_yleft) + + left_fit_cr = np.polyfit(plot_yleft * y_mppx, plot_xleft * x_mppx, 2) + right_fit_cr = np.polyfit(plot_yright * y_mppx, plot_xright * x_mppx, 2) + + left_curverad = ((1 + (2 * left_fit_cr[0] * y_eval * y_mppx + left_fit_cr[1]) ** 2) ** 1.5) / np.absolute( + 2 * left_fit_cr[0]) + right_curverad = ((1 + ( + 2 * right_fit_cr[0] * y_eval * y_mppx + right_fit_cr[1]) ** 2) ** 1.5) / np.absolute( + 2 * right_fit_cr[0]) + + return left_curverad, right_curverad + + + def fit_polynom(mask, lx, rx, ly, ry): + # Sol ve sağ noktaları al + points_left = [(lx[i], ly[i]) for i in range(min(len(lx), len(ly)))] + points_right = [(rx[i], ry[i]) for i in range(min(len(rx), len(ry)))] + + # Sol taraf için polinom uydur + left_x = np.array([point[0] for point in points_left]) + left_y = np.array([point[1] for point in points_left]) + left_poly = np.polyfit(left_y, left_x, 2) + + # Sağ taraf için polinom uydur + right_x = np.array([point[0] for point in points_right]) + right_y = np.array([point[1] for point in points_right]) + right_poly = np.polyfit(right_y, right_x, 2) + + # Türev hesapla + left_turev = np.polyder(left_poly) + right_turev = np.polyder(right_poly) + + x_point = 200 # Örneğin x = 2 noktasındaki eğimi bulalım + slope_at_x_left = np.polyval(left_turev, x_point) + slope_at_x_right = np.polyval(right_turev, x_point) + + #print("Yolun Eğimi\n") + #print(f"sol_türev {left_turev}") + #print(f"sag_türev {right_turev}\n") + #print(f"x = {x_point} noktasında eğim sol {slope_at_x_left}") + #print(f"x = {x_point} noktasında eğim sag {slope_at_x_right}\n") + + left_curverad, right_curverad = compute_curvature([left_poly, right_poly], y_mppx=0.002, x_mppx=0.002) + #print(f"Left Curve {left_curverad}") + #print(f"Right Curve {right_curverad}") + + + # Mask üzerine eğrileri çiz + for y in range(mask.shape[0]): + left_x = int(np.polyval(left_poly, y)) + right_x = int(np.polyval(right_poly, y)) + cv2.circle(mask, (left_x, y), 2, (255, 0, 0), -1) # Sol şerit + cv2.circle(mask, (right_x, y), 2, (0, 0, 255), -1) # Sağ şerit + + # # Sol köşenin eğimini göster + # if 0 < y < mask.shape[0] - 1: + # left_gradient = left_turev[y] + # if abs(left_gradient) > 0.1: # Eğim eşik değeri + # cv2.putText(mask, f'{left_gradient:.2f}', (left_x, y), cv2.FONT_HERSHEY_SIMPLEX, 0.5, + # (0, 255, 0), 1, cv2.LINE_AA) + # + # # Sağ köşenin eğimini göster + # right_gradient = right_turev[y] + # if abs(right_gradient) > 0.1: # Eğim eşik değeri + # cv2.putText(mask, f'{right_gradient:.2f}', (right_x, y), cv2.FONT_HERSHEY_SIMPLEX, 0.5, + # (0, 255, 0), 1, cv2.LINE_AA) + + + return mask, left_poly, right_poly, left_curverad, right_curverad + + def draw(mask, poly_param, curve_rad, offset): + ''' + Utility function to draw the lane boundaries and numerical estimation of lane curvature and vehicle position. + :param mask (ndarray): Original image + :param poly_param (ndarray): Set of 2nd order polynomial coefficients that represent the detected lane lines + :param curve_rad (float32): Lane line curvature + :param offset (float32): Car offset + :return (ndarray): Image with visual display + ''' + + left_fit = poly_param[0] + right_fit = poly_param[1] + plot_xleft, plot_yleft, plot_xright, plot_yright = get_poly_points(left_fit, right_fit) + + pts_left = np.array([np.transpose(np.vstack([plot_xleft, plot_yleft]))]) + pts_right = np.array([np.flipud(np.transpose(np.vstack([plot_xright, plot_yright])))]) + + # Write data on the image + if (left_fit[1] + right_fit[1]) / 2 > 0.05: + text = 'Left turn, curve radius: {:04.2f} m'.format(curve_rad) + elif (left_fit[1] + right_fit[1]) / 2 < -0.05: + text = 'Right turn, curve radius: {:04.2f} m'.format(curve_rad) + else: + text = 'Straight' + + cv2.putText(mask, text, (50, 60), cv2.FONT_HERSHEY_DUPLEX, 1.2, (255, 255, 255), 2, cv2.LINE_AA) + + direction = '' + if offset > 0: + direction = 'left' + elif offset < 0: + direction = 'right' + + text = '{:0.1f} cm {} of center'.format(abs(offset) * 100, direction) + cv2.putText(mask, text, (50, 110), cv2.FONT_HERSHEY_DUPLEX, 1.2, (255, 255, 255), 2, cv2.LINE_AA) + + return mask + + + def sliding_windows(mask, image_org): + # Histogram + histogram = np.sum(mask[mask.shape[0] // 2:, :], axis=0) + midpoint = int(histogram.shape[0] / 2) + left_base = np.argmax(histogram[:midpoint]) + right_base = np.argmax(histogram[midpoint:]) + midpoint + + # Sliding Window + y = 472 + lx = [] + rx = [] + ly = [] + ry = [] + + msk = mask.copy() + + while y > 0: + ## Left threshold + img = mask[y - 40:y, left_base - 50:left_base + 50] + contours, _ = cv2.findContours(img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) + for contour in contours: + M = cv2.moments(contour) + if M["m00"] != 0: + cx = int(M["m10"] / M["m00"]) + cy = int(M["m01"] / M["m00"]) + lx.append(left_base - 50 + cx) + ly.append(y) + left_base = left_base - 50 + cx + + ## Right threshold + img = mask[y - 40:y, right_base - 50:right_base + 50] + contours, _ = cv2.findContours(img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) + for contour in contours: + M = cv2.moments(contour) + if M["m00"] != 0: + cx = int(M["m10"] / M["m00"]) + cy = int(M["m01"] / M["m00"]) + rx.append(right_base - 50 + cx) + ry.append(y) + right_base = right_base - 50 + cx + + cv2.rectangle(image_org, (left_base - 50, y), (left_base + 50, y - 40), (0, 255, 255), 2) + cv2.rectangle(image_org, (right_base - 50, y), (right_base + 50, y - 40), (0, 255, 255), 2) + y -= 40 + return msk, lx, rx, ly, ry + + def calculate_steering_angle(con_right, + con_left): # Sag Taraf Conturlerini ve sol taraf conturlerini alarak steering angle hesaplar + steering_line_threshold = 250 # Yeşil nokta sınır + #print(f"Con Right {con_right}\n Con Left {con_left}") + filt_con_right = {key: value for key, value in con_right.items() if + key > steering_line_threshold} # Piksel değeri y'de 250 (260-270-280 gibi) altında olanları almak + filt_con_left = {key: value for key, value in con_left.items() if key > steering_line_threshold} + + min_x_right = {} + max_x_left = {} + + for key in filt_con_right.keys(): + if len(filt_con_right[key]) > 0: + min_x_right[key] = min(filt_con_right[key]) + + # filt_min_x_right = {key: value for key, value in min_x_right.items() if value < img_width/2 + x_axis_offset} # X ekseninde offset ile tam ortaya steering line çekilmesini engelliyorum + + for key in filt_con_left.keys(): + if len(filt_con_left[key]) > 0: + max_x_left[key] = max(filt_con_left[key]) + + # filt_max_x_left = {key: value for key, value in max_x_left.items() if value > img_width/2 - x_axis_offset} + + #print(f"Min X Right {min_x_right}\n Max X Left {max_x_left}") + + no_lines_left = False + no_lines_right = False + + if len(list(max_x_left.keys())) > 0: + sol_taraf_max_y = min(list(max_x_left.keys())) + sol_taraf_max_x = max_x_left[sol_taraf_max_y] + else: + # print("Sol Taraf Max X Yok") + no_lines_left = True + sol_taraf_max_y = 300 + sol_taraf_max_x = 0 + if len(list(min_x_right.keys())) > 0: + sag_taraf_max_y = min(list(min_x_right.keys())) + left_offset = img_width / 2 + sag_taraf_max_x = min_x_right[sag_taraf_max_y] + left_offset + else: + #print("Sağ Taraf Max X Yok") + no_lines_right = True + sag_taraf_max_y = 300 + sag_taraf_max_x = 640 + + if sol_taraf_max_y > sag_taraf_max_y or no_lines_left: + if no_lines_left: + steering_point = [0, steering_line_threshold] + else: + steering_point = [int((sol_taraf_max_x + sag_taraf_max_x) / 2), + int((sol_taraf_max_y + sag_taraf_max_y) / 2)] + #print(f"Sola dön - Sol taraf max_y = {sol_taraf_max_y} - Sağ Taraf max_y = {sag_taraf_max_y}") + #print(f"Sola dön - Sol taraf max_x = {sol_taraf_max_x} - Sağ Taraf max_x = {sag_taraf_max_x}") + elif sol_taraf_max_y < sag_taraf_max_y or no_lines_right: + if no_lines_right: + steering_point = [640, steering_line_threshold] + else: + steering_point = [int((sol_taraf_max_x + sag_taraf_max_x) / 2), + int((sol_taraf_max_y + sag_taraf_max_y) / 2)] + # print(f"Sağa dön - Sol taraf max_y = {sol_taraf_max_y} - Sağ Taraf max_y = {sag_taraf_max_y}") + # print(f"Sağa dön - Sol taraf max_x = {sol_taraf_max_x} - Sağ Taraf max_x = {sag_taraf_max_x}") + else: + # print(f"Düz git - Sol taraf max_y = {sol_taraf_max_y} - Sağ Taraf max_y = {sag_taraf_max_y}") + # print(f"Düz git - Sol taraf max_x = {sol_taraf_max_x} - Sağ Taraf max_x = {sag_taraf_max_x}") + steering_point = [int((sol_taraf_max_x + sag_taraf_max_x) / 2), + int((sol_taraf_max_y + sag_taraf_max_y) / 2)] + + print(f"Steering Point {steering_point}") + bottom_mid = [320, 480] + #cv2.line(merged_img, steering_point, bottom_mid, color=(0, 255, 0), thickness=2) + steering_angle = math.atan((steering_point[0] - bottom_mid[0]) / (steering_point[1] - bottom_mid[1])) + steering_angle = 90 - (90 - math.degrees(steering_angle)) + self.steering_angle = steering_angle + green_text = "\033[92m" # green + reset_text = "\033[0m" # reset to default color + print(f"{green_text}Steering Angle {self.steering_angle}{reset_text}") + # 180 - (90 - math.degrees(steering_angle)) + + steering_angle_limit = 20 + if self.steering_angle > steering_angle_limit: + self.steering_angle = steering_angle_limit + elif self.steering_angle < -steering_angle_limit: + self.steering_angle = -steering_angle_limit + + steering_angle_error_th = 3 + for prev_steer_angle in self.collective_steering_angle: + if abs(prev_steer_angle - self.steering_angle) < steering_angle_error_th: + steering_angle = self.collective_steering_angle[-1] + + # 30 kere aynı değer geldiyse dön artık yeter be adam + for prev_steer_angle in self.collective_steering_angle: + if prev_steer_angle != steering_angle: + break + + if len(self.collective_steering_angle) < 30: + self.collective_steering_angle.append(self.steering_angle) + else: + collective_steering_angle = [0] + + # self.twist.angular.z = float(math.degrees(self.steering_angle)) + # self.twist.linear.x = float(0.2) + + def draw_mean_circle(grup_x_given, img): + points_green = [] + for key in grup_x_given.keys(): + if grup_x_given[key] == []: + continue + # print(grup_x_given[key]) + x1x2 = grup_x_given[key] + # print(f"x1x2 Tipi = {type(x1x2)}") + x_mean = int((x1x2[0] + x1x2[1]) / 2) + cv2.circle(img, (x_mean, key), 1, (0, 255, 0), thickness=2) + points_green.append((x_mean, key)) + return points_green + + def draw_lines_with_pixels(points, img): + # En az iki nokta gereklidir + if len(points) < 2: + print("En az iki nokta gereklidir.") + return [], img + + line_pixels = [] # Tüm çizgi pikselleri burada saklanacak + + # İlk iki noktayı al + p1 = points[0] + p2 = points[1] + + # İlk iki noktadan bir çizgi çiz + cv2.line(img, p1, p2, (255, 0, 0), thickness=2) + + # İlk iki nokta arasındaki pikselleri ekle + line_pixels.extend(get_line_pixels(p1, p2)) + + # Kalan noktaları işleme + for i in range(2, len(points)): + # Geçerli noktayı al + p = points[i] + + # Son noktadan çizgi çek + cv2.line(img, p2, p, (255, 0, 0), thickness=2) + + # Çizginin piksellerini ekle + line_pixels.extend(get_line_pixels(p2, p)) + + # Sonraki çizgi için p2'yi güncelle + p2 = p + + # İşlenmiş görüntüyü ve çizgi piksellerini döndür + return line_pixels + + def get_line_pixels(p1, p2): + line_pixels = [] # Çizginin piksellerini burada saklanacak + x1, y1 = p1 + x2, y2 = p2 + dx = abs(x2 - x1) + dy = abs(y2 - y1) + x, y = x1, y1 + sx = -1 if x1 > x2 else 1 + sy = -1 if y1 > y2 else 1 + + if dx > dy: + err = dx / 2.0 + while x != x2: + line_pixels.append((x, y)) + err -= dy + if err < 0: + y += sy + err += dx + x += sx + else: + err = dy / 2.0 + while y != y2: + line_pixels.append((x, y)) + err -= dx + if err < 0: + x += sx + err += dy + y += sy + line_pixels.append((x, y)) + return line_pixels + + img_sliding_window, lx, rx, ly, ry = sliding_windows(imgDilPers, imgWarp) + mask_with_curve, left_poly, right_poly, left_curverad, right_curverad = fit_polynom(imgWarp, lx=lx, rx=rx, ly=ly, ry=ry) + curvature = (left_curverad + right_curverad) / 2 + offset_calc = compute_offset_from_center([left_poly, right_poly], x_mppx=0.002) + draw(mask=mask_with_curve, poly_param=[left_poly, right_poly], curve_rad=curvature, offset=offset_calc) + steering_angle_in, steering_angle_out = calculate_steering_angle_new(L=0.6, R=curvature, T=0.2) + self.steering_angle = math.degrees(steering_angle_in) + #steering_point = calculate_steering_angle_new_oldstyle(lx, rx, ly, ry) + #cv2.circle(mask_with_curve, (int(steering_point[0]), int(steering_point[1])), 5, (255 ,0, 255), -1) + + # print(f"lx :\n {lx}") + # print(f"rx :\n {rx}") + # print(f"ly :\n {ly}") + # print(f"ry :\n {ry}") + + # cv2.circle(img, bottom_left, 5, (0, 0, 255), -1) + # cv2.circle(img, bottom_right, 5, (0, 0, 255), -1) + # cv2.circle(img, top_left, 5, (0, 0, 255), -1) + # cv2.circle(img, top_right, 5, (0, 0, 255), -1) + end_time = time.time() + duration = end_time - start_time # İşlemin sürdüğü süreyi hesapla + print(f"CallbackImage süresi: {duration} saniye.") + cv2.imshow('Contour', imgContourPers) + cv2.imshow("img-given", img) + cv2.imshow('Perspective', mask_with_curve) + cv2.imshow("SlidingWindows", img_sliding_window) + print("steering_angle",self.steering_angle) + #self.steerLateral = 0.2 + self.carControlPublisher() + + + cv2.waitKey(3) + # image_message = self.bridge.cv2_to_imgmsg(img, "bgr8") + + # print(len(image_message.data)) + + # cv2.imshow("IMG Wrap Right", imgWarpRight) + # cv2.imshow("IMG Wrap Left", imgWarpLeft) + + # world_coords = calculate_real_world_coor(matrix, contoursPers) + # + # print("World Coordinates: ", world_coords) + # print("World Coordinates Shape: ", world_coords.shape) + # + # # İşaretlenmiş dünya koordinatlarını normal görüntüde işaretleme + # img_world_coor = img.copy() + # cv2.circle(img_world_coor, (int(world_coords[0]), int(world_coords[1])), 5, (0, 255, 0), -1) + # + # cv2.imshow("imgWorldCoords", img_world_coor) + + except CvBridgeError as e: + print(e) + + def pose_callback(self, data): + pass + # start_time = time.time() + # print("Position: ", data.pose.position) + # print("Orientation: ", data.pose.orientation) + # end_time = time.time() + # duration = end_time - start_time # İşlemin sürdüğü süreyi hesapla + # print(f"CallbackPosition süresi: {duration} saniye.") + + # ser = serial.Serial(port='/dev/ttyACM0',baudrate=19200) + # #switchOn=input('Hiz Degerini Giriniz') + # switchOn="#1:20;;\r\n" + # ser.write(bytes(switchOn,'utf-8')) + # time.sleep(1) + # value=ser.readline() + # valueInString=str(value,'UTF-8') + # print(valueInString) + + def carControlPublisher(self): + + ser = serial.Serial(port='/dev/ttyACM0',baudrate=19200) + #switchOn=input('Hiz Degerini Giriniz') + switchOn="#1:0;;\r\n" + ser.write(bytes(switchOn,'utf-8')) + #time.sleep(1) + value=ser.readline() + valueInString=str(value,'UTF-8') + print(valueInString) + + if self.steering_angle > 20: + self.steering_angle = 20 + if self.steering_angle < -20: + self.steering_angle = -20 + + ser = serial.Serial(port='/dev/ttyACM0',baudrate=19200) + #switchOn=input('Hiz Degerini Giriniz') + switchOn = "#2:{};;\r\n".format(self.steering_angle) + ser.write(bytes(switchOn,'utf-8')) + #time.sleep(1) + value=ser.readline() + valueInString=str(value,'UTF-8') + #print(valueInString) + + # carData = {} + # carData = { + # 'action': '2', + # 'steerAngle': self.steering_angle + # # 'steerAngle': 30.0 + # } + # # print("data",carData) + # # print("math.degrees(float(self.DM2Arr(self.u[0, 1]))) ",math.degrees(float(self.DM2Arr(self.u[0, 1]))) ) + # self.carData = json.dumps(carData) + # self.carControl.publish(self.carData) + # #rospy.sleep(0.01) + # car2Data = {} + # # İkinci mesaj için veriler + # car2Data = { + # 'action': '1', + # 'speed': self.steerLateral + # #'speed': 0.0 + # } + # #print("data",car2Data) + # self.car2Data = json.dumps(car2Data) + # self.carControl.publish(self.car2Data) + + +def main(): + rospy.init_node('cameratracker', anonymous=False) + cameratracker = realCamera(camera_topic_name = "/zed2i/zed_node/stereo/image_rect_color", + pose_topic_name = "/zed2i/zed_node/pose") + + rate = rospy.Rate(30) # 10Hz + try: + while not rospy.is_shutdown(): + # Sürekli çalışacak kodlar burada + rate.sleep() + except KeyboardInterrupt: + cv2.destroyAllWindows() + + +if __name__ == '__main__': + main() + diff --git a/src/example/src/realmpc.py b/src/example/src/realmpc.py new file mode 100755 index 0000000..b23b579 --- /dev/null +++ b/src/example/src/realmpc.py @@ -0,0 +1,1861 @@ +#!/usr/bin/env python3 + +# Copyright (c) 2019, Bosch Engineering Center Cluj and BFMC organizers +# All rights reserved. + +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: + +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. + +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. + +# 3. Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. + +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE + +import rospy +import cv2 +import numpy as np +import torch +from sensor_msgs.msg import Image +from cv_bridge import CvBridge, CvBridgeError +import math +from geometry_msgs.msg import Twist +from std_msgs.msg import String +import json +from geometry_msgs.msg import PoseStamped +import serial +import time +import math +from math import atan2 +from std_msgs.msg import String +import json +from nav_msgs.msg import Odometry +import xml.etree.ElementTree as ET +import pandas as pd +from utils.msg import localisation, IMU + +from casadi import sin, cos, pi +import casadi as ca +import pandas as pd + +from tf.transformations import euler_from_quaternion, quaternion_from_euler + +from example.msg import MekatronomYolo +#from ultralytics import YOLO +from obstacle_detector.msg import Obstacles +import sys + +class mpc(): + # ===================================== INIT========================================== + def __init__(self,camera_topic_name,pose_topic_name): + """ + Creates a bridge for converting the image from Gazebo image intro OpenCv image + """ + + #Parameters + self.debug = rospy.get_param('~debug', True) + self.hue_min = rospy.get_param('~hue_min', 0) + self.hue_max = rospy.get_param('~hue_max', 60) + self.sat_min = rospy.get_param('~sat_min', 0) + self.sat_max = rospy.get_param('~sat_max', 255) + self.val_min = rospy.get_param('~val_min', 70) + self.val_max = rospy.get_param('~val_max', 255) + # self.graphml_file_path = rospy.get_param('~graphml_file_path', '/home/mekatronom/boschmekatronom_ws/BoschMekatronom/src/example/src/Competition_track_graph.graphml') + self.PathSituations = False + self.mpc_started = False + self.localisation_CB = False + self.IMU_cb = False + + #For checking behaviour tree + self.last_update_time = rospy.Time.now() + self.last_update_time_obstacles_checking = rospy.Time.now() + self.last_path_index = 0 + self.last_target_update_time = None + self.current_target = None + + self.yolo_data = MekatronomYolo() + + self.state = "keep lane" + # self.intercept_targets = ['416','440','470', '486', '58', '6', '42', '20', '26', '58','451','452','454','453','455','456'] + self.intercept_targets = [] + self.distance_flag = False + self.yolo_intercept_flag = False + self.park_scenerio = False + + self.prev_time = rospy.Time.now().to_sec() + self.bridge = CvBridge() + + #sükrü pathfinding icin + self.source_node="800" + self.target_node="459" + #newPathFinding + self.graphml_file_path = rospy.get_param('~graphml_file_path', '/home/mekatronom/boschmekatronom_ws/BoschMekatronom/src/example/src/fixed.graphml')#sollamalı + self.file_path_original ='/home/mekatronom/boschmekatronom_ws/BoschMekatronom/src/example/src/gercek.graphml' + self.obs_dontuse=["489","127","96"]#kullanma listesi + self.yolvar=False + self.parking_nodes_id = ["900","901","902","903"] + self.parking_spot_is_full = [] + self.parkings_are_available =[] + self.expath = [] + + self.flagsolla = 0 + self.obstaclesayac = 0 + self.center_x = [] + self.center_y = [] + self.obstacles_array = [] + self.past_obs_dontuse = [] + self.pathGoalsYawDegreecalled = False + self.process_and_publish_data("800","459")#burda çağırılmış ilk defa her çıkmaz sokakta yine çağırıcam + + ##############################3sükrü + #ForPathFinding + self.file_path = rospy.get_param('~file_path', self.graphml_file_path) + self.data_pub = rospy.Publisher('~graph_data', String, queue_size=10) + + # self.bridge = CvBridge() + self.cv_image = np.zeros((640, 480)) + + #subscribers + # self.rgb=rospy.Subscriber(camera_topic_name,Image,self.image_callback) + self.pose=rospy.Subscriber(pose_topic_name,PoseStamped,self.pose_callback) + self.yolo_intercept_sub = rospy.Subscriber('/automobile/yolo', MekatronomYolo, self.yolo_intercept_callback) + self.obstacleDetector_sub = rospy.Subscriber('/obstacles', Obstacles, self.obstacleDetector_callback) + + #timerCB + self.behaviourTimer = rospy.Timer(rospy.Duration(0.5), self.behaviourTimerCallback) + self.mpcTimer = rospy.Timer(rospy.Duration(0.05), self.mpcTimerCallback) + + print("mpc node") + self.device = 'cuda' if torch.cuda.is_available() else 'cpu' + print(f"Device: {self.device}") + + + def DM2Arr(self,dm): + return np.array(dm.full()) + + def shift_timestep(self): + + # rospy.loginfo('shift_time_step') + + self.state_init = ca.DM([self.position_x, self.position_y, self.yaw_rad]) + #print("self.u[1, :].T",self.u[1, :].T) + f_value = self.f(self.state_init,self.u[0, :].T) + + # print("f_Value",f_value) + self.next_state = ca.DM.full(self.state_init + (self.step_horizon * f_value)) + # print("self.initial_state",self.state_init) + #print("self.next_state",self.next_state) + # print("self.staate_init",self.state_init) + # print("self.state_target",self.state_target) + self.t0 = self.t0 + self.step_horizon + self.u0 = ca.vertcat(self.u[1:, :], self.u[-1, :]) + + #print("self.u0",self.u0) + + def mpc_start_settings(self): + # print("mpcsettings") + + if self.park_scenerio: + rospy.loginfo("SETTINGS TO PARK PARAMS FOR MPC") + Q_x = rospy.get_param('~Q_x', 1) + Q_y = rospy.get_param('~Q_y', 1) + Q_theta = rospy.get_param('~Q_theta', 0.1) + R1 = rospy.get_param('~R1', 0.02) + R2 = rospy.get_param('~R2', 0.08) + step_horizon = rospy.get_param('~step_horizon', 0.1) + N = rospy.get_param('~N', 16) + rob_diam = rospy.get_param('~rob_diam', 0.354) + wheel_radius = rospy.get_param('~wheel_radius', 1) + L_value = rospy.get_param('~Lx', 0.3) + Ly = rospy.get_param('~Ly', 0.04) + v_max = rospy.get_param('~v_max', 0.16) + v_min = rospy.get_param('~v_min', -0.25) + omega_max = rospy.get_param('~omega_max', pi/7.5) #degree + omega_min = rospy.get_param('~omega_min', -pi/7.5) #degree + self.iteration = 0 + self.steeringRad = 0.0 + + else: + rospy.loginfo("INITIAL KEEP LANE PARAMS FOR MPC") + + Q_x = rospy.get_param('~Q_x', 1) + Q_y = rospy.get_param('~Q_y', 1) + Q_theta = rospy.get_param('~Q_theta', 1) + R1 = rospy.get_param('~R1', 1.0) + R2 = rospy.get_param('~R2', 1.8) + step_horizon = rospy.get_param('~step_horizon', 0.2) + N = rospy.get_param('~N', 12) + rob_diam = rospy.get_param('~rob_diam', 0.354) + wheel_radius = rospy.get_param('~wheel_radius', 1) + L_value = rospy.get_param('~Lx', 0.4) + Ly = rospy.get_param('~Ly', 0.04) + v_max = rospy.get_param('~v_max', 0.22) + v_min = rospy.get_param('~v_min', -0.1) + omega_max = rospy.get_param('~omega_max', pi/7.5) #degree + omega_min = rospy.get_param('~omega_min', -pi/7.5) #degree + self.iteration = 0 + self.steeringRad = 0.0 + + rotationMatrix = np.array([[cos(self.yaw_rad),-sin(self.yaw_rad)], + [sin(self.yaw_rad),cos(self.yaw_rad)]]) + matrix = np.array([1,4]) + self.Q_x,self.Q_y = np.dot(rotationMatrix,matrix) + + print("Q_x",Q_x,"Q_y", Q_y ) + + ######eklenen yeni kod: + + nodes_x = np.array([node[1] for node in self.pathGoalsYawDegree]) + nodes_y = np.array([node[2] for node in self.pathGoalsYawDegree]) + + distances = np.sqrt((nodes_x - self.position_x)**2 + (nodes_y - self.position_y)**2) + min_distance, index = min((val, idx) for (idx, val) in enumerate(distances)) + closest_node_id = self.pathGoalsYawDegree[index][0] + self.last_path_index = index + + # current_id = closest_node_id + if self.park_scenerio: + current_id = self.current_id + print("parking scenerio") + + else: + current_id = "800" # yeni eklediğim current_id + + matching_pairs = [pair for pair in self.SourceTargetNodes if pair[0] == current_id] + next_id = matching_pairs[0][1] + matching_entry = next((entry for entry in self.pathGoalsYawDegree if entry[0] == next_id), None) + print("matching_entry",matching_entry) + target_x, target_y = matching_entry[1], matching_entry[2] + target_y = target_y + #print("target_x",target_x) + #print("target_y",target_y) + dx = target_x - self.position_x + dy = target_y - self.position_y + yaw = atan2(dy, dx) + ###### + + state_init = ca.DM([self.position_x, self.position_y, self.yaw_rad]) # initial state + state_target = ca.DM([target_x, target_y, yaw]) + + #bu eklediğim state_target kısmıyla buradan state_target girmene gerek kalmaması lazım. sadece zed'e konum girsen olur. + + + #state_target = ca.DM([6.07, 13.72-13.04, 0.0]) # target state + #state_target = ca.DM([9.1, 13.72-13.07, 0.0]) #parking scenerio + #state_target = ca.DM([0.5, 0.0, 0.0]) + + # state symbolic variables + x = ca.SX.sym('x') + y = ca.SX.sym('y') + theta = ca.SX.sym('theta') + states = ca.vertcat( + x, + y, + theta + ) + n_states = states.numel() + + #new define + v = ca.SX.sym('v') + omega = ca.SX.sym('omega') + controls = ca.vertcat( + v, + omega + ) + n_controls = controls.numel() + # print("n_controls",n_controls) + + # matrix containing all states over all time steps +1 (each column is a state vector) + X = ca.SX.sym('X', n_states, (N + 1)) + + # matrix containing all control actions over all time steps (each column is an action vector) + U = ca.SX.sym('U', n_controls, N) + # print(U) + # coloumn vector for storing initial state and target state + P = ca.SX.sym('P', n_states + n_states) + + # state weights matrix (Q_X, Q_Y, Q_THETA) + Q = ca.diagcat(Q_x, Q_y, Q_theta) + + + # controls weights matrix + R = ca.diagcat(R1, R2) + + # discretization model (e.g. x2 = f(x1, v, t) = x1 + v * dt) + rot_3d_z = ca.vertcat( + ca.horzcat(cos(theta), -sin(theta), 0), + ca.horzcat(sin(theta), cos(theta), 0), + ca.horzcat( 0, 0, 1) + ) + # Mecanum wheel transfer function which can be found here: + # https://www.researchgate.net/publication/334319114_Model_Predictive_Control_for_a_Mecanum-wheeled_robot_in_Dynamical_Environments + # J = (wheel_radius/4) * ca.DM([ + # [ 1, 1, 1, 1], + # [ -1, 1, 1, -1], + # [-1/(Lx+Ly), 1/(Lx+Ly), -1/(Lx+Ly), 1/(Lx+Ly)] + # ]) + # RHS = states + J @ controls * step_horizon # Euler discretization + #RHS = rot_3d_z @ J @ controls + + L = ca.SX.sym('L') + + RHS = ca.vertcat( + v * ca.cos(theta), + v * ca.sin(theta), + v / L_value * ca.tan(omega) + ) + # maps controls from [va, vb, vc, vd].T to [vx, vy, omega].T + f = ca.Function('f', [states, controls], [RHS]) + + + cost_fn = 0 # cost function + g = X[:, 0] - P[:n_states] # constraints in the equation + + st = X[:,0] + # print("st",st) + # runge kutta + #print(P[3:6]) + print("g",g) + print("st",st) + + for k in range(N): + st = X[:, k] + con = U[:, k] + print("con",con) + print("st",st) + #print(R) + # + cost_fn = cost_fn + ca.mtimes(ca.mtimes((st - P[3:6]).T, Q), (st - P[3:6])) + ca.mtimes(ca.mtimes(con.T, R), con) + + st_next = X[:, k+1] + #print("st_next",st_next) + k1 = f(st, con) + k2 = f(st + (step_horizon/2)*k1, con) + k3 = f(st + (step_horizon/2)*k2, con) + k4 = f(st + step_horizon * k3, con) + + + st_next_RK4 = st + (step_horizon / 6) * (k1 + 2 * k2 + 2 * k3 + k4) + print("st_next_RK4",st_next_RK4) + + g = ca.vertcat(g, st_next - st_next_RK4) + + obs_x = -3.0 + obs_y = -3.0 + obs_diam = 0.3 + print(g.shape) + + #Engelden Kaçma constraints eklenmesi + + # for k in range(N+1): # In Python, range goes from 0 to N, so we use range(N+1) for 1 to N+1 + # # Calculate the squared distance from the robot to the obstacle + # distance_squared = (X[0, k] - obs_x)**2 + (X[1, k] - obs_y)**2 + # # The constraint ensures the robot stays outside the sum of the semi-diameters + # constraint = -ca.sqrt(distance_squared) + (rob_diam/2 + obs_diam/2) + # # Append the constraint to the list + # g = ca.vertcat(g,constraint) + + print(g.shape) + # Vertically concatenate all constraints into a CasADi vector + #print("g",g) + #print("st",st) + #print("con",con) + + OPT_variables = ca.vertcat( # Example: 3x11 ---> 33x1 where 3=states, 11=N+1 + ca.reshape(X,3*(N+1),1), + ca.reshape(U,2*N, 1) + ) + #print("opt_Variables",OPT_variables) + + nlp_prob = { + 'f': cost_fn, + 'x': OPT_variables, + 'g': g, + 'p': P + } + + opts = { + 'ipopt': { + 'max_iter': 100, + 'print_level': 0, + 'acceptable_tol': 1e-8, + 'acceptable_obj_change_tol': 1e-6 + }, + 'print_time': 0 + } + + #print("nlp_prob",nlp_prob) + + solver = ca.nlpsol('solver', 'ipopt', nlp_prob, opts) + + print("solver",solver) + + lbx = ca.DM.zeros((n_states*(N+1) + n_controls*N, 1)) + ubx = ca.DM.zeros((n_states*(N+1) + n_controls*N, 1)) + lbg = ca.DM.zeros((n_states*(N+1), 1)) + ubg = ca.DM.zeros((n_states*(N+1), 1)) + + # lbg[n_states*(N+1)+1: 3*(N+1) + (N+1)] = -ca.inf + # ubg[n_states*(N+1)+1: 3*(N+1) + (N+1)] = 0 + + lbx[0: n_states*(N+1): n_states] = -ca.inf # X lower bound + lbx[1: n_states*(N+1): n_states] = -ca.inf # Y lower bound + lbx[2: n_states*(N+1): n_states] = -ca.inf # theta lower bound + + ubx[0: n_states*(N+1): n_states] = ca.inf # X upper bound + ubx[1: n_states*(N+1): n_states] = ca.inf # Y upper bound + ubx[2: n_states*(N+1): n_states] = ca.inf # theta upper bound + + lbx[n_states*(N+1):n_states*(N+1)+2*N:2] = v_min # v lower bound for all V + ubx[n_states*(N+1):n_states*(N+1)+2*N:2] = v_max # v upper bound for all V + lbx[n_states*(N+1)+1:n_states*(N+1)+2*N:2] = omega_min # omega bound for all V + ubx[n_states*(N+1)+1:n_states*(N+1)+2*N:2] = omega_max # omega bound for all V + + + #print("lbx",lbx) + #print("ubx",ubx) + + args = { + 'lbg': lbg, # constraints lower bound + 'ubg': ubg, # constraints upper bound + 'lbx': lbx, + 'ubx': ubx + } + + #print('Size of lbg:', args['lbg'].shape) + + + + # print("args",args) + self.t0 = 0 + + # print("ilk_target",state_target) + # print("ilk_konum",state_init) + + # xx = DM(state_init) + t = ca.DM(self.t0) + + u0 = ca.DM.zeros(N, 2) # initial control + X0 = ca.repmat(state_init, 1, N+1).T # initial state full + #print("u0",u0) + + cat_states = self.DM2Arr(X0) + #cat_controls = self.DM2Arr(u0[0, :]) + cat_controls = np.array([]).reshape(0, u0.shape[1]) + + self.times = np.array([[0]]) + + # print("ilk_u0",u0) + # print("ilk_X0",X0) + # print("state_init",state_init) + # print("cat_states",cat_states) + # print("cat_controls",cat_controls) + + self.step_horizon = step_horizon + self.cat_controls = cat_controls + self.cat_states = cat_states + self.t = t + self.f = f + self.solver = solver + self.state_init =state_init + self.state_target = state_target + self.n_states = n_states + self.N = N + self.X0 =X0 + self.args = args + self.u0 = u0 + self.n_controls = n_controls + self.v_min = v_min + self.v_max = v_max + self.omega_min = omega_min + self.omega_max = omega_max + + def mpcTimerCallback(self,event): + try: + + if self.localisation_CB == True and self.IMU_cb == True: + + if not self.mpc_started: + print("mpc started") + self.mpc_start_settings() + self.mpc_started = True + print("mpc settings finished") + self.prev_time = rospy.Time.now().to_sec() + + # lbx = ca.DM.zeros((self.n_states*(self.N+1) + self.n_controls*self.N, 1)) + # ubx = ca.DM.zeros((self.n_states*(self.N+1) + self.n_controls*self.N, 1)) + # lbg = ca.DM.zeros((self.n_states*(self.N+1), 1)) + # ubg = ca.DM.zeros((self.n_states*(self.N+1), 1)) + + # # lbg[self.n_states*(self.N+1)+1: 3*(self.N+1) + (self.N+1)] = -ca.inf + # # ubg[self.n_states*(self.N+1)+1: 3*(self.N+1) + (self.N+1)] = 0 + + # lbx[0: self.n_states*(self.N+1): self.n_states] = -ca.inf # X lower bound + # lbx[1: self.n_states*(self.N+1): self.n_states] = -ca.inf # Y lower bound + # lbx[2: self.n_states*(self.N+1): self.n_states] = -ca.inf # theta lower bound + + # ubx[0: self.n_states*(self.N+1): self.n_states] = ca.inf # X upper bound + # ubx[1: self.n_states*(self.N+1): self.n_states] = ca.inf # Y upper bound + # ubx[2: self.n_states*(self.N+1): self.n_states] = ca.inf # theta upper bound + + # lbx[self.n_states*(self.N+1):self.n_states*(self.N+1)+2*self.N:2] = self.v_min # v lower bound for all V + # ubx[self.n_states*(self.N+1):self.n_states*(self.N+1)+2*self.N:2] = self.v_max # v upper bound for all V + # lbx[self.n_states*(self.N+1)+1:self.n_states*(self.N+1)+2*self.N:2] = self.omega_min # omega bound for all V + # ubx[self.n_states*(self.N+1)+1:self.n_states*(self.N+1)+2*self.N:2] = self.omega_max # omega bound for all V + + + # #print("lbx",lbx) + # #print("ubx",ubx) + + # args = { + # 'lbg': lbg, # constraints lower bound + # 'ubg': ubg, # constraints upper bound + # 'lbx': lbx, + # 'ubx': ubx + # } + + + # self.args = args + + self.args['p'] = ca.vertcat( + self.state_init, # current state + self.state_target # target state + ) + + self.args['x0'] = ca.vertcat( + ca.reshape(self.X0.T, self.n_states*(self.N+1), 1), + ca.reshape(self.u0.T, self.n_controls*self.N, 1) + ) + + # print("self.args['x0']",self.args['x0']) + # print("self.args['p']",self.args['p']) + # print("self.u0",self.u0.T) + # print("self.X0",self.X0.T) + + sol = self.solver( + x0=self.args['x0'], + lbx=self.args['lbx'], + ubx=self.args['ubx'], + lbg=self.args['lbg'], + ubg=self.args['ubg'], + p=self.args['p'] + ) + + self.u = ca.reshape((sol['x'][self.n_states * (self.N + 1):]).T, self.n_controls, self.N).T + + + # Daha sonra yeniden şekillendirin + + # print("self.u",self.u) + + self.cat_states = np.dstack(( + self.cat_states, + self.DM2Arr(self.X0) + )) + + self.cat_controls = np.vstack(( + self.cat_controls, + self.DM2Arr(self.u[0, :]) + )) + + + self.shift_timestep() + #print("output_shift_time_step") + self.X0 = ca.reshape((sol['x'][: self.n_states * (self.N+1)]).T, self.n_states, self.N+1).T + # print("self.X0 ",self.X0) + + self.X0 = ca.vertcat( + self.X0[1:, :], + ca.reshape(self.X0[-1, :], 1, -1) + ) + + # xx ... + + # print("u",self.u) + # print("uygulanan kontrol degree",self.DM2Arr(self.u[0, 1])) + # self.twist.angular.z = float(self.DM2Arr(self.u[0, 1])) + # self.twist.linear.x = float(self.DM2Arr(self.u[0, 0])) + + if self.state == "keep lane": + self.steerAngle = math.degrees(float(self.DM2Arr(self.u[0, 1]))) + self.steerLateral = float(self.DM2Arr(self.u[0, 0])) + self.carControlPublisher() + + + #print("self.u",self.u) + # print("self.state_init",self.state_init) + # print("self.state_target",self.state_target) + # print("self.next_state",self.next_state) + #print("self.pathGoalsYawDegree:",self.pathGoalsYawDegree) + + state_target_slice = self.state_target[:2] + state_init_slice = self.state_init[:2] + + # Calculate the norm + distance = ca.norm_2(ca.DM(state_init_slice) - ca.DM(state_target_slice)) + + + if distance < 0.25: + #print("test3") + print("insideofdistance") + + nodes_x = np.array([node[1] for node in self.pathGoalsYawDegree]) + nodes_y = np.array([node[2] for node in self.pathGoalsYawDegree]) + #print("nodes_x",nodes_x ) + #print("nodes_y",nodes_y) + + distances = np.sqrt((nodes_x - self.position_x)**2 + (nodes_y - self.position_y)**2) + min_distance, index = min((val, idx) for (idx, val) in enumerate(distances)) + closest_node_id = self.pathGoalsYawDegree[index][0] + print("closest_node_id",closest_node_id) + + self.last_path_index = index + current_id = closest_node_id + self.current_id = current_id + + sign_looking_band = [] + + nodes_x_for_obstacles = np.array([node[1] for node in self.pathGoalsYawDegreeCopy]) + nodes_y_for_obstacles = np.array([node[2] for node in self.pathGoalsYawDegreeCopy]) + #print("nodes_x",nodes_x ) + #print("nodes_y",nodes_y) + + distances = np.sqrt((nodes_x_for_obstacles - self.position_x)**2 + (nodes_y_for_obstacles - self.position_y)**2) + min_distance, index = min((val, idx) for (idx, val) in enumerate(distances)) + current_id_for_obstacles = self.pathGoalsYawDegreeCopy[index][0] + + for i in range(1, 4): + matching_pairs_signs = [pair for pair in self.SourceTargetNodesCopy if pair[0] == current_id_for_obstacles] + matching_entry = next((entry for entry in self.pathGoalsYawDegreeCopy if entry[0] == current_id_for_obstacles), None) + # print("self.path_original",self.path_original) + # print("self.SourceTargetNodes",self.SourceTargetNodes) + print("testing") + print("matching_entry",matching_entry) + print("matcihing_pairs_signs",matching_pairs_signs) + if matching_pairs_signs: + next_id_signs = matching_pairs_signs[0][1] + matching_entry_second = next((entry for entry in self.pathGoalsYawDegreeCopy if entry[0] == next_id_signs), None) + + print("matching_entry_second",matching_entry_second) + print("next_id",next_id_signs) + + sign_looking_band.append(current_id_for_obstacles) + current_id_for_obstacles = next_id_signs + + TargetPosition = matching_entry[1:3] + # TargetPositionNext = matching_entry_second[1:3] + + x_diff = abs(matching_entry[1] - matching_entry_second[1]) + y_diff = abs(matching_entry[2] - matching_entry_second[2]) + + x_threshold = 0.13 + y_threshold = 0.13 + + + for center_x, center_y in zip(self.center_x, self.center_y): + ObstaclePosition = [center_x, center_y] + + # if in self.parking_nodes_id: + # self.parking_spot_is_full.append(matching_entry[0]) + + # Calculate the norm + obstaclediff = np.linalg.norm(np.array(ObstaclePosition) - np.array(TargetPosition)) + + is_within_x_threshold = min(matching_entry[1], matching_entry_second[1]) - x_threshold <= center_x <= max(matching_entry[1], matching_entry_second[1]) + x_threshold + is_within_y_threshold = min(matching_entry[2], matching_entry_second[2]) - y_threshold <= center_y <= max(matching_entry[2], matching_entry_second[2]) + y_threshold + print("obstaclediff",obstaclediff) + print("center_x",center_x) + print("center_y",center_y) + print("is_within_x_threshold",is_within_x_threshold) + print("is_within_y_threshold",is_within_y_threshold) + + if obstaclediff < 0.15 or (is_within_x_threshold and is_within_y_threshold): + + print("obstaclediffINSIDE",obstaclediff) + print("current_idObstacle",self.current_id) + print("matching_entryObstacle",matching_entry[0]) + + self.past_obs_dontuse = self.obs_dontuse.copy() + + # self.obstacles_array = [] ## bu 2 kod sornadan kaldırılacaktır. + # self.obs_dontuse=["489","127","96"]#kullanma listesi + + if matching_entry[0] not in self.obstacles_array: + self.obstacles_array.append(matching_entry[0]) + for obstacle_id in self.obstacles_array: + print("self.obs_dontuseinside",self.obs_dontuse) + print("self.obstacles_arrayinside",self.obstacles_array) + obstacle_id_str = str(obstacle_id) + if obstacle_id_str not in self.obs_dontuse: + self.obs_dontuse.insert(0, obstacle_id_str) + if self.obs_dontuse != self.past_obs_dontuse: + self.process_and_publish_data(self.source_node,self.target_node)#curent den global targete göndericem + self.last_update_time_obstacles_checking = rospy.Time.now() + + else: + print("obs_dontuse same") + + print("self.obs_dontuse",self.obs_dontuse) + print("self.obstacles_array",self.obstacles_array) + + if self.parking_spot_is_full: + self.parkings_are_available = list(set(self.parking_nodes_id) - set(self.parking_spot_is_full)) + print("self.parkings_are_available",self.parkings_are_available) + + + if self.obstacles_array and self.flagsolla == 0: + for obstacle_id in self.obstacles_array: + # Code to execute for each obstacle + # ... + + matching_entry_obstacle_first = next((entry for entry in self.pathGoalsYawDegreeCopy if entry[0] == obstacle_id), None) + matching_entry_id_second = [pair for pair in self.SourceTargetNodesCopy if pair[0] == obstacle_id] + matching_entry_obstacle_second = next((entry for entry in self.pathGoalsYawDegreeCopy if entry[0] == matching_entry_id_second[0][1]), None) + # print("self.pathGoalsYawDegree",self.pathGoalsYawDegree) + # print("matching_entry_obstacle",matching_entry_obstacle_first) + print("obstacle_id",obstacle_id) + targetposition_obstacle_first = matching_entry_obstacle_first[1:3] + targetposition_obstacle_second = matching_entry_obstacle_second[1:3] + print("firstcalculation",np.linalg.norm(np.array(targetposition_obstacle_first) - np.array(ObstaclePosition))) + print("secondcalculation",np.linalg.norm(np.array(targetposition_obstacle_second) - np.array(ObstaclePosition))) + if np.linalg.norm(np.array(targetposition_obstacle_first) - np.array(ObstaclePosition)) < 0.16 or np.linalg.norm(np.array(targetposition_obstacle_second) - np.array(ObstaclePosition)) < 0.16: + self.state = "waiting the obstacle move on" + self.last_update_time_obstacles_checking = rospy.Time.now() + self.obstaclesayac = 0 + + print("self.statefirst",self.state) + print("self.flagsolla",self.flagsolla) + self.obstaclesayac = self.obstaclesayac + 1 + + if self.state == "keep lane" and self.obstacles_array and self.flagsolla == 0 and self.obstaclesayac == 10: + + print("hi im here") + print("self.obstacles_array",self.obstacles_array) + for value in self.obstacles_array: + if value in self.obs_dontuse: + self.obs_dontuse.remove(value) + self.obstacles_array = [] + + self.process_and_publish_data(self.source_node,self.target_node)#curent den global targete göndericem + + # if any obstacle in the matching_pairs_signs create new path so call djikstra + + if any(sign_id in self.intercept_targets for sign_id in sign_looking_band) and "stop" in self.yolo_data.object: + + if current_id in self.intercept_targets: + self.steerAngle = math.degrees(0.0) + self.steerLateral = float(0.0) + self.state = "waiting on intercept" + # rospy.loginfo("self.state: {}".format(self.state)) + self.carControlPublisher() + + else: + self.last_update_time = rospy.Time.now() + + elif any(sign_id in self.intercept_targets for sign_id in sign_looking_band) and "crosswalk" in self.yolo_data.object: + + self.steerAngle = math.degrees(float(self.DM2Arr(self.u[0, 1]))) + self.steerLateral = float(0.09) + # Assuming self.yolo_data.distance is an array of floats (float32[]) + # Check if the array is not empty and then process the distances + distance_meets_criteria = False + # if self.yolo_data.distance: + # # If you want to check if any distance meets your criteria + # # Example: Check if any distance is within the specific range + # if any(0.3 < d < 0.7 for d in self.yolo_data.distance): + # distance_meets_criteria = True + # print("test1") + # else: + # distance_meets_criteria = False + # print("test2") + + # Use distance_meets_criteria for your logic + # if distance_meets_criteria and current_id in self.intercept_targets: + # self.steerAngle = math.degrees(float(self.DM2Arr(self.u[0, 1]))) + # self.steerLateral = float(0.0) + # self.state = "obstacles on crosswalk" + # else: + # self.state = "no obstacle on crosswalk" + + rospy.loginfo("self.state: {}".format(self.state)) + + self.carControlPublisher() + print("test3") + self.last_update_time = rospy.Time.now() + print("test4") + + # elif (current_id == '1' or current_id == "143"): + + # distance_meets_criteria = False + # if self.yolo_data.distance: + # # If you want to check if any distance meets your criteria + # # Example: Check if any distance is within the specific range + # if any(0.3 < d < 0.8 for d in self.yolo_data.distance): + # distance_meets_criteria = True + # print("test1") + # print("inside of 1") + # else: + # distance_meets_criteria = False + # print("test2") + # # Use distance_meets_criteria for your logic + # if distance_meets_criteria: + # self.steerAngle = math.degrees(float(0.0)) + # self.steerLateral = float(0.0) + # self.state = "obstacles on ID: 42" + # rospy.loginfo("self.state: {}".format(self.state)) + # print("test5") + + # self.carControlPublisher() + # print("test3") + # self.last_update_time = rospy.Time.now() + # print("test4") + + else: + self.state = "keep lane" + rospy.loginfo("self.state: {}".format(self.state)) + + print("yolo_data.object",self.yolo_data.object) + if any(sign_id in self.intercept_targets for sign_id in sign_looking_band) and "parking" in self.yolo_data.object: + self.mpc_start_settings() + self.park_scenerio = True + rospy.loginfo("self.state: park section id:1") + + matching_pairs = [pair for pair in self.SourceTargetNodes if pair[0] == current_id] + print("matching_pairs",matching_pairs) + if not matching_pairs: + self.distance_flag = False + state_target_slice = self.state_target[:3] + + state_init_slice = self.state_init[:3] + + # Calculate the norm + distance = ca.norm_2(ca.DM(state_init_slice) - ca.DM(state_target_slice)) + if distance < 0.15: + self.state_target = ca.DM([self.position_x, self.position_y, self.yaw_rad]) + self.last_update_time = rospy.Time.now() + + if matching_pairs: + next_id = matching_pairs[0][1] + # print("next_id",next_id) + #print("self.pathGoalsYawDegree:",self.pathGoalsYawDegree) + #for i in range(1, 5): + + matching_entry = next((entry for entry in self.pathGoalsYawDegree if entry[0] == next_id), None) + if matching_entry: + target_x, target_y = matching_entry[1], matching_entry[2] + target_y = target_y + #print("target_x",target_x) + #print("target_y",target_y) + dx = target_x - self.position_x + dy = target_y - self.position_y + yaw = atan2(dy, dx) + # print("yaw",yaw) + + if pi < yaw: + changing_Value = yaw - pi + yaw = -pi + changing_Value + + if -pi > yaw: + changing_Value = yaw + pi + yaw = pi + changing_Value + + if pi < self.yaw_rad: + changing_Value = self.yaw_rad - pi + self.yaw_rad = -pi + changing_Value + + if -pi > self.yaw_rad: + changing_Value = self.yaw_rad + pi + self.yaw_rad = pi + changing_Value + + bandwidth = 1.57 + + if self.yaw_rad + bandwidth > pi and yaw < -1.57: + cars_value = pi - self.yaw_rad + goal_value = pi - abs(yaw) + yaw = cars_value + goal_value + self.yaw_rad + if self.yaw_rad - bandwidth < -pi and yaw > 1.57: + goal_value = pi - yaw + yaw = -pi - goal_value + + print(f'Step {1} -> ID: {next_id}, X: {target_x:.2f}, Y: {target_y:.2f}, Yaw: {yaw:.2f} rad') + + if next_id == '55': + self.distance_flag = False + else: + self.distance_flag = True + + if next_id == '459': + self.state_target = ca.DM([target_x, target_y, -1.57]) + self.distance_flag = False + else: + self.state_target = ca.DM([target_x, target_y, yaw]) + + + # Engelle olan mesafeyi kontrol et + # is_close_to_any_obstacle = any( + # np.sqrt((x0[0] - ox)**2 + (x0[1] - oy)**2) < 0.6 for ox, oy in zip(obs_x1, obs_y1) + # ) + + # if i == 2 and is_close_to_any_obstacle: + # ObstacleDetected = 1 + # elif i == 1 and not ObstacleDetected: + # xs = np.array([target_x, target_y, yaw]) + # elif i == 4 and ObstacleDetected: + # xs = np.array([target_x, target_y, yaw]) + + # if i in [3, 1, 2, 4] and not is_close_to_any_obstacle: + # ObstacleDetected = 0 + # print("current_id",current_id) + # print("next_id",next_id) + + + + self.goal_id = matching_entry + if self.state == "keep lane": + self.last_update_time = rospy.Time.now() + + if self.distance_flag == True and self.goal_id != '459': + matching_pairs = [pair for pair in self.SourceTargetNodes if pair[0] == self.current_id] + next_id = matching_pairs[0][1] + matching_entry = next((entry for entry in self.pathGoalsYawDegree if entry[0] == next_id), None) + if matching_pairs: + target_x, target_y = matching_entry[1], matching_entry[2] + target_y = target_y + dx = target_x - self.position_x + dy = target_y - self.position_y + yaw = atan2(dy, dx) + + if pi < yaw: + changing_Value = yaw - pi + yaw = -pi + changing_Value + + elif -pi > yaw: + changing_Value = yaw + pi + yaw = pi + changing_Value + + if pi < self.yaw_rad: + changing_Value = self.yaw_rad - pi + self.yaw_rad = -pi + changing_Value + + elif -pi > self.yaw_rad: + changing_Value = self.yaw_rad + pi + self.yaw_rad = pi + changing_Value + + bandwidth = 1.57 + + if self.yaw_rad + bandwidth > pi and yaw < -1.57: + cars_value = pi - self.yaw_rad + goal_value = pi - abs(yaw) + yaw = cars_value + goal_value + self.yaw_rad + elif self.yaw_rad - bandwidth < -pi and yaw > 1.57: + goal_value = pi - yaw + yaw = -pi - goal_value + + self.state_target = ca.DM([target_x, target_y, yaw]) + + if self.debug: + self.debug_callback() + + # State Vector [x y yaw v s]' + + + except AttributeError as e: + print(e) + rospy.logerr(e) + + def yolo_intercept_callback(self,data): + + self.yolo_data.object = data.object + # print("self.yolo_data.object",self.yolo_data.object) + self.yolo_data.distance = data.distance + # print("self.yolo_data.distance",self.yolo_data.distance) + + def behaviourTimerCallback(self,event): + # rospy.loginfo("behaviourTimerCallback") + + if rospy.Time.now() - self.last_update_time_obstacles_checking >= rospy.Duration(10.0): + self.last_update_time_obstacles_checking = rospy.Time.now() + + if self.obstacles_array: + print("hi im here behavior") + print("self.obstacles_array",self.obstacles_array) + for value in self.obstacles_array: + if value in self.obs_dontuse: + self.obs_dontuse.remove(value) + self.obstacles_array = [] + + self.process_and_publish_data(self.source_node,self.target_node)#curent den global targete göndericem + self.last_update_time_obstacles_checking = rospy.Time.now() + + if rospy.Time.now() - self.last_update_time >= rospy.Duration(5.0): + # rospy.loginfo(f"ID 8 saniye boyunca değişmedi: {self.goal_id}") + # rospy.loginfo("behaviourTimerCallback inside") + + if self.state == "waiting on intercept": + + self.state = "keep lane" + matching_pairs = [pair for pair in self.SourceTargetNodes if pair[0] == self.goal_id[0]] + + if matching_pairs: + next_id = matching_pairs[0][1] + + #print("self.pathGoalsYawDegree:",self.pathGoalsYawDegree) + #for i in range(1, 5): + + matching_entry = next((entry for entry in self.pathGoalsYawDegree if entry[0] == next_id), None) + self.goal_id = matching_entry + if matching_entry: + target_x, target_y = matching_entry[1], matching_entry[2] + target_y = target_y + #print("target_x",target_x) + #print("target_y",target_y) + dx = target_x - self.position_x + dy = target_y - self.position_y + yaw = atan2(dy, dx) + + if pi < yaw: + changing_Value = yaw - pi + yaw = -pi + changing_Value + + elif -pi > yaw: + changing_Value = yaw + pi + yaw = pi + changing_Value + + if pi < self.yaw_rad: + changing_Value = self.yaw_rad - pi + self.yaw_rad = -pi + changing_Value + + elif -pi > self.yaw_rad: + changing_Value = self.yaw_rad + pi + self.yaw_rad = pi + changing_Value + + + bandwidth = 1.57 + + if self.yaw_rad + bandwidth > pi and yaw < -1.57: + cars_value = pi - self.yaw_rad + goal_value = pi - abs(yaw) + yaw = cars_value + goal_value + self.yaw_rad + elif self.yaw_rad - bandwidth < -pi and yaw > 1.57: + goal_value = pi - yaw + yaw = -pi - goal_value + + print(f'Step {1} -> ID: {next_id}, X: {target_x:.2f}, Y: {target_y:.2f}, Yaw: {yaw:.2f} rad') + + #self.state_target = ca.DM([target_x, target_y, 0.0]) + + self.state_target = ca.DM([target_x, target_y, yaw]) + self.last_update_time = rospy.Time.now() + + if self.state == "no obstacle on crosswalk": + self.state = "no obstacle on crosswalk" + self.last_update_time = rospy.Time.now() + + if self.state == "obstacles on ID: 42": + self.steerAngle = math.degrees(float(self.DM2Arr(self.u[0, 1]))) + self.steerLateral = float(0.00) + distance_meets_criteria = False + if self.yolo_data.distance: + # If you want to check if any distance meets your criteria + # Example: Check if any distance is within the specific range + if any(0.3 < d < 0.7 for d in self.yolo_data.distance): + distance_meets_criteria = True + else: + distance_meets_criteria = False + # Use distance_meets_criteria for your logic + if distance_meets_criteria: + self.steerAngle = math.degrees(float(0.0)) + self.steerLateral = float(0.0) + self.state = "obstacles on ID: 42" + rospy.loginfo("self.state: {}".format(self.state)) + else: + self.state = "keep lane" + + + self.carControlPublisher() + self.last_update_time = rospy.Time.now() + + + if self.state == "obstacles on crosswalk": + self.steerAngle = math.degrees(float(self.DM2Arr(self.u[0, 1]))) + self.steerLateral = float(0.09) + # Assuming self.yolo_data.distance is an array of floats (float32[]) + # Check if the array is not empty and then process the distances + distance_meets_criteria = False + if self.yolo_data.distance: + # If you want to check if any distance meets your criteria + # Example: Check if any distance is within the specific range + if any(0.3 < d < 0.7 for d in self.yolo_data.distance): + distance_meets_criteria = True + else: + distance_meets_criteria = False + + # Use distance_meets_criteria for your logic + if distance_meets_criteria and self.current_id in self.intercept_targets: + self.steerAngle = math.degrees(float(self.DM2Arr(self.u[0, 1]))) + self.steerLateral = float(0.0) + self.state = "obstacles on crosswalk" + else: + self.state = "no obstacle on crosswalk" + + rospy.loginfo("self.state: {}".format(self.state)) + + self.carControlPublisher() + self.last_update_time = rospy.Time.now() + # else: + # if rospy.Time.now() - self.last_update_time >= rospy.Duration(8.0) and self.goal_id: + # matching_pairs = [pair for pair in self.SourceTargetNodes if pair[0] == self.goal_id[0]] + # print("matching_pairs",matching_pairs) + + # if matching_pairs: + # next_id = matching_pairs[0][1] + + # #print("self.pathGoalsYawDegree:",self.pathGoalsYawDegree) + # #for i in range(1, 5): + + # matching_entry = next((entry for entry in self.pathGoalsYawDegree if entry[0] == next_id), None) + # self.goal_id = matching_entry + # if matching_entry: + # target_x, target_y = matching_entry[1], matching_entry[2] + # target_y = target_y + # #print("target_x",target_x) + # #print("target_y",target_y) + # dx = target_x - self.position_x + # dy = target_y - self.position_y + # yaw = atan2(dy, dx) + + # if pi < yaw: + # changing_Value = yaw - pi + # yaw = -pi + changing_Value + + # elif -pi > yaw: + # changing_Value = yaw + pi + # yaw = pi + changing_Value + + # if pi < self.yaw_rad: + # changing_Value = self.yaw_rad - pi + # self.yaw_rad = -pi + changing_Value + + # elif -pi > self.yaw_rad: + # changing_Value = self.yaw_rad + pi + # self.yaw_rad = pi + changing_Value + + + # bandwidth = 1.57 + + # if self.yaw_rad + bandwidth > pi and yaw < -1.57: + # cars_value = pi - self.yaw_rad + # goal_value = pi - abs(yaw) + # yaw = cars_value + goal_value + self.yaw_rad + # elif self.yaw_rad - bandwidth < -pi and yaw > 1.57: + # goal_value = pi - yaw + # yaw = -pi - goal_value + + # print(f'Step {1} -> ID: {next_id}, X: {target_x:.2f}, Y: {target_y:.2f}, Yaw: {yaw:.2f} rad') + + # #self.state_target = ca.DM([target_x, target_y, 0.0]) + + # self.state_target = ca.DM([target_x, target_y, yaw]) + + else: + pass + # def image_callback(self, data): + # try: + + # if self.localisation_CB == True and self.IMU_cb == True: + # # start_time = time.time() + # # img = self.bridge.imgmsg_to_cv2(data, "bgr8") + # # gray_image = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) + # # img_width = 640 + # # img_height = 480 + # # #img = cv2.resize(img, (img_width, img_height)) + # # cv2.imshow('color', img) + # # cv2.waitKey(3) + # # if self.debug: + # # self.debug_callback() + # # pass + # pass + # except AttributeError and CvBridgeError as e: + # print(e) + # rospy.logerr(e) + + + #OLD# new path finding + + + + + + ##################### NEW PATH FINDING ####################### + def process_and_publish_data(self,temp_source,temp_target): + # GraphML dosyasını okuma + + + #self.obs_dontuse=["360","314","321","344","315","270","367","64","169"] + + stx = time.time()# dosyadan endge okuma işelmleri zaman ölçümü için + #0.003 saniye sürüyo her bi okuma her çağırmada 2 defa çağırmak yerine + #normal olanı başta bir defa çağırıp her engel tespiti yapıp + # self.obs_dontuseya eleman eklediğimdede öbürünü çağırsam toplam süre 0.008 den 0.005 lere gerileyebilir + #edge ve node datalarını fonksiyonun dışından almam gerekir + + + + tree = ET.parse(self.file_path_original) + root = tree.getroot() + #print("root : ",root) + # Düğüm verilerini çıkarma + self.nodes_data = self.extract_nodes_data(root) + self.edges_data, self.edges_data_true_ilkverisyon = self.extract_edges_data(root) + + #print("nodes data ::::",self.edges_data) + flagsolla=0 + self.flagsolla = 0 + for ciz in self.edges_data:#self.obs_dontuse listesindekilerden herhangi biri kesikli çizgi üstüne geliyo mu kontrolü + for kx in self.obs_dontuse: + if ciz[1]==kx and ciz[2]==True: + flagsolla=1 + self.flagsolla = 1 + break#fazladan dolaşmasın bi kere bulması yeterli + #print("################ : ",ciz[2]) + + etx = time.time() + elapsed_timex = etx - stx + print('Execution time:', elapsed_timex, 'seconds##################-----------------####################') + + + + + if flagsolla==1: + #sollama kısmı için özel graph dosyasını açıcam ikinci bi edge çıkarma işlemi umarım fazla yavaşlatmaz + #şu anda 0.008 saniye + tree2 = ET.parse(self.file_path) + root2 = tree2.getroot() + #print("root : ",root2) + self.nodes_data = self.extract_nodes_data(root2) + self.edges_data, self.edges_data_true_ilkverisyon = self.extract_edges_data(root2) + + + + #self.obs_dict = {node[0]: (node[1], node[2]) for node in self.nodes_data}#node dataya yeni düğümleri oluşturup burda self.nodes_data nın yerine koyucam + #obs_dict bütün nodeların sıra ile numarasını ve içidede x ve y var + #print("//////////////////////////////////") + #print("node datas::::::",self.obs_dict)#burda noktlalarındictonaryde id: (x,y) formatında + + + noded,edged=self.extract_graph()# (nodedict,edgedict) döndürür + #print("edgedictionary ",edged) + #self.obs_dontuse=["360","314","321","344","315","270","367","64","169","326"] + path_short=self.dijkstra(temp_source,temp_target,noded,edged,self.obs_dontuse)#nodedictionary =noded + self.path_original = path_short + newnodedictionary,stlist=self.beizer(path_short,noded)#beizere targete giden path i veriyorum + self.obs_dict = newnodedictionary#yeni node sozlüğü + self.edges_data_true =stlist#source target list + #print("-----------------------------------------") + print(stlist) + + + #print("-----------------------------------------") + #nodedata ,sourcetarget + #beizer çıktısı 1 : source targetnodes tipinde path 2:yeni eklenen noktalarıda içeren node_data o datayıda self.obs_dicte vericem + #409 da döngüye kapılıyo çıkamıyo + + self.SourceTargetNodes = [(edge[0], edge[1]) for edge in self.edges_data_true if edge[2]] + #self.SourceTargetNodes edge formatında benim pathi edge formatına çevirmemlazım + #burda sourceın içine hedef yolu vermek için edges data ture formatında en kısa yolu ustte vermemlazım + #print("sorutceTrgetnodes:",self.edges_data_true) + #print("**************************************************") + #print("**************************************************") + + self.path = [] + for source_id, target_id in self.SourceTargetNodes: + if target_id in self.obs_dict: + coords = self.obs_dict[target_id] + self.path.append((target_id, coords[0], coords[1])) + #print("self.path::::",self.path) + #print("**************************************************") + #print("**************************************************") + + + + # Her bir nokta için bir sonraki nokta ile arasındaki açıyı hesaplama + angles = [] + for i in range(len(self.path) - 1): + dx = self.path[i + 1][1] - self.path[i][1] + dy = self.path[i + 1][2] - self.path[i][2] + angle = math.atan2(dy, dx) + angles.append(angle) + + if angles: # Check if angles list is not empty + angles.append(angles[-1]) + + self.pathGoalsYawDegree = [(*p, angle) for p, angle in zip(self.path, angles)] + + if not self.pathGoalsYawDegreecalled: + self.pathGoalsYawDegreeCopy = self.pathGoalsYawDegree + self.SourceTargetNodesCopy = self.SourceTargetNodes + self.pathGoalsYawDegreecalled = True + + #print("raw edges data : ",self.edges_data) + #print("self.SourceTargetNodes", self.SourceTargetNodes) + #print("self.obs_dict", self.obs_dict) + #print("**************************************************") + #print("**************************************************") + #print("**************************************************") + #print("YawDegree", self.pathGoalsYawDegree) + #print("self.path", self.path) + #print(self.nodes_data + + + + # Data publishing simulation + data_message = str(self.edges_data_true) + # print("****************************************") + # print("****************************************") + # print("****************************************") + # print("****************************************") + + # print("Graph data published:", data_message) + + def extract_nodes_data(self, root): + nodes_data = [] + for node in root.findall(".//{http://graphml.graphdrawing.org/xmlns}node"): + node_id = node.get('id') + d0 = None + d1 = None + for data in node: + if data.get('key') == 'd0': + d0 = float(data.text) # 'x' koordinatı + elif data.get('key') == 'd1': + d1 = 13.72-float(data.text) # 'y' koordinatı + if d0 is not None and d1 is not None: + nodes_data.append((node_id, d0, d1)) + return nodes_data + + def extract_graph(self): + #print(self.edges_data) + # ('source','target',mesafe) + inf = sys.maxsize + graph_temp=self.edges_data + + nodedict=dict([]) + edgedict=dict([])#keyler source valuelerde (target,mesafe) + for node in self.nodes_data: + nodedict[node[0]]={'mesafe':inf,'atalist':[],'solla':False,'x':node[1],'y':node[2]}#0 1 di 1 2 olarak düzelttim + #print(" nokta :::::::::::::::::::::::::::::::::::node:",node,"x:",node[1]," y:",node[2]) + # key i id olan sırası ile mesafe ,parent(en kısay yoldaki köken düğüm), gezeldimi(0,1) tutan dictionary + for edge in graph_temp: + # print(edge[0],edge[1]) + # sozlüğe yerleştirirken 1 key için var mi diye kontrol edicem varsa targeti valueya ekliycem yoksa oluşturup targeti valeuya ekliycem + #edge[0] source : edge + temp=edge[0] + edgemesafe =1#edge 0 ve edge 1 i verip ikisi arası mesafeyi vericem + if temp in edgedict.keys(): + #targeti valeuya append et mesafeleri de burda value içinde tutmamlazım + #edgedict[edge[0]]=(edgedict[edge[0]],(edge[1])) + edgedict[edge[0]].append([edge[1],1]) + + else: + edgedict[edge[0]]=[[edge[1],1]] + #tuple dan list tipine cevirdim + + #sozlukte yoksa da ekliycek + #print("edgedict :", edge[0]," :",edgedict[edge[0]]) + edgetemp =edgedict[edge[0]] + #for i in edgetemp: + # print("test",i," leng :") + #print(edgedict[edge[0]]) + # bağzı düğümler dosyadada tekrara düşmüş ikitane 88 var + return (nodedict,edgedict) + def minyol(self,edge_curent_source): + # print(edge_curent_source) + #print(edge_curent_source[0]) + #şimdilik her edge ağırlığı 1 diye önce 1. yi seçicek ama sonradan mesafe hesapolı edgelerde bu kısmı lazım + print(edge_curent_source[0][1]) + min=edge_curent_source[0][1] + min_id=edge_curent_source[0][0] + for edge in edge_curent_source: + if edge[1]2 : + befbagend=self.expath[indexnoEX-2]#sourn burda olabillir tek nokta kalan path de indexi bir geriye alıp patlıyor olabilir + else : + befbagend=self.expath[indexnoEX]#bunu test et + print("before bagend",befbagend) + + #bagend in bi oncesinin ata listesi ni versem yeticek + #print("test ::",edgedictt) + + print("bagend :::",bagend) + #print("bag path :",nodedictt[bagend]['atalist'])#burda bagend deki nokta ya eski source dan ulaşamadığımız için atalistesi boş ama bi on cesini vermem lazim benim + nodedictt[befbagend]['atalist']=nodedictt[befbagend]['atalist']+[befbagend] + return nodedictt[befbagend]['atalist'] + + + return nodedictt[target]['atalist'] + + def plotbfmc(self,node_dat,path): + #dosya adı için + obj = time.localtime(1627987508.6496193) + gren=[0,255,0] + orginx=0 + orginy=3863 + time_str = time.asctime(obj) + name='/home/mekatronom/out/'+time_str + #print("data:",node_dat) + #print("path:",path) + img=cv2.imread('/home/mekatronom/Downloads/Competition_track_graph.png') + #cv2.circle(img, (0,3863), 20, gren, -1) + for idxy in path: + x=int(orginx+node_dat[idxy][0]*282) + y=int(orginy-node_dat[idxy][1]*282) + #print("x:",x,type(x)) + #print("y:",y,type(y)) + cv2.circle(img, (x,y), 15, gren, -1) + #cv2.circle(img, (400,800), 15, gren, -1) + + #cv2.imshow('img', img) + cv2.imwrite(name+".png", img) + cv2.waitKey(1) + + def beizer(self,path,node_d): + #node_d nin y si hatalı geliyo ama araba düzgün sürüyo ?????????? 105 de y değiştirilmiş d1 = 13.72-float(data.text) # 'y' koordinatı + #hem beizeri hemde source targetteki gibi edge formatına getiricem + #print("node data _d:",node_d)#nose_dict node_d çok kalabalık daha saddeleştirip dictionary id: (x,y),(x,y) formatına getiricem + #print("----------------------") + #print("--------------------------------------------------------------------------------") + #print("node data 470 :",node_d['470']['x']," ",node_d['470']['y']) + #print("-----********************************--------------------------------------------------") + new_node_data={} + for idkey in node_d.keys(): + #print(idkey) + new_node_data.update({idkey: (node_d[idkey]["x"],node_d[idkey]["y"])}) + #print("x:",x) + #print("new_node_data:",new_node_data) + new_point_counter=600#yeni düğmleri çakışma olmasın diye 600 den başlattım + new_path=path.copy()#path in kopyasını aldım değişiklikleri ona uyguluycam itarete ederken list den eleman silip ekliyince hata veriyo + + + + #beizer1:verilen path de beizer uygulanması gereken yerleri bul açı kontrolü + print("beizer path :",path) + + + #print("----------------------") + path_length=len(path) + for f in range(path_length-2): + + #açı hesaplıycam + if node_d[path[f]]["x"]==node_d[path[f+1]]["x"]: + #sıfıra bölme durumu + angel_rad1=1.57#radyan cinsinden 90derece + else: + angel_rad1=math.atan((node_d[path[f]]["y"]-node_d[path[f+1]]["y"])/(node_d[path[f]]["x"]-node_d[path[f+1]]["x"]))#arctan + #math.atan radayan cinsinden a numeric value between -PI/2 and PI/2 radians. + #print("y deki fark :",abs(node_d[path[f]]["y"]-node_d[path[f+1]]["y"]),"x deki fark:",abs(node_d[path[f]]["x"]-node_d[path[f+1]]["x"])) + #print("x1:",node_d[path[f]]["x"],"x2:",node_d[path[f+1]]["x"],"y1:",node_d[path[f]]["y"],"y2:",node_d[path[f+1]]["y"]) + angel_deg1=angel_rad1*57.3 + #print("açı bir :",angel_deg1) + + if node_d[path[f+1]]["x"]==node_d[path[f+2]]["x"]: + #sıfıra bölme durumu + angel_rad2=1.57#radyan cinsinden 90derece + else: + angel_rad2=math.atan((node_d[path[f+1]]["y"]-node_d[path[f+2]]["y"])/(node_d[path[f+1]]["x"]-node_d[path[f+2]]["x"]))#arctan + #math.atan radayan cinsinden a numeric value between -PI/2 and PI/2 radians. + #print("y deki fark :",abs(node_d[path[f]]["y"]-node_d[path[f+1]]["y"]),"x deki fark:",abs(node_d[path[f]]["x"]-node_d[path[f+1]]["x"])) + #print("x1:",node_d[path[f]]["x"],"x2:",node_d[path[f+1]]["x"],"y1:",node_d[path[f]]["y"],"y2:",node_d[path[f+1]]["y"]) + angel_deg2=angel_rad2*57.3 + #print("açı iki :",angel_deg2) + + b_andgel=abs(angel_deg1-angel_deg2) + #print("birinci açı :",angel_deg1," ikinci açı :",angel_deg2) + + if b_andgel>55 and b_andgel<110 : + #print("--------------------------------------------------------------------------------------------------------------") + #print("keskin dönüş :: başlangiç",path[f]," köşe noktası",path[f+1]," bitiş",path[f+2]," f+2:",f+2,"aradaki açı:",b_andgel) + + #lineer(lstart,lstop,t)#lineer fonksiyon da başlangıç ve bitiş arasında her çağırldığında t kadar ilerleyip ilerleyen nokta kordinatları döndüren fonk + #print("koşe x:",node_d[path[f+1]]["x"],"y:",node_d[path[f+1]]["y"]) + numPts=2 + #iflerin orda keskin köşelere hardkodladım düzeltme gerekebilir o 3 nokta için sadece bu 3 nokta da hardkodlama var # number of points in Bezier Curve + if path[f]=='30'and path[f+1]=='35' and path[f+2]=='27': + controlPts=[[node_d[path[f]]["x"],node_d[path[f]]["y"]],[4.60,4.30],[node_d[path[f+2]]["x"],node_d[path[f+2]]["y"]]]# control points + + elif path[f]=='58'and path[f+1]=='62' and path[f+2]=='55': + controlPts=[[node_d[path[f]]["x"],node_d[path[f]]["y"]],[4.60,0.70],[node_d[path[f+2]]["x"],node_d[path[f+2]]["y"]]]# control points + + elif path[f]=='6'and path[f+1]=='11' and path[f+2]=='3': + controlPts=[[node_d[path[f]]["x"],node_d[path[f]]["y"]],[4.60,7.10],[node_d[path[f+2]]["x"],node_d[path[f+2]]["y"]]]# control points + else: + controlPts=[[node_d[path[f]]["x"],node_d[path[f]]["y"]],[node_d[path[f+1]]["x"],node_d[path[f+1]]["y"]],[node_d[path[f+2]]["x"],node_d[path[f+2]]["y"]]]# control points + t=np.array([i*1/numPts for i in range(0,numPts+1)]) + + B_x=(1-t)*((1-t)*controlPts[0][0]+t*controlPts[1][0])+t*((1-t)*controlPts[1][0]+t*controlPts[2][0])#yeni noktaların x i + B_y=(1-t)*((1-t)*controlPts[0][1]+t*controlPts[1][1])+t*((1-t)*controlPts[1][1]+t*controlPts[2][1])#yeni y si + temp_new_nodelist=[] + for new_p in range(1,numPts):#+1 yapıp bütün noktaları yazdırdım yeni düğüm eklerken 1 den numPts ye kadar gezip yeni düğüm ekliycem + #once yeni noktaları new_node_data ya ekliycem + new_point_counter=new_point_counter+1 + new_point_str=str(new_point_counter) + #print("yeni nokta ",new_p,"x:",B_x[new_p],"y:",B_y[new_p]) + new_node_data.update({new_point_str: (B_x[new_p],B_y[new_p])}) + temp_new_nodelist.append(new_point_str)#nodu temp liste ekliyorum + # print("bx:",B_x.tolist()) + # print("by:",B_y.tolist()) + # print("stop x:",node_d[path[f+2]]["x"],"y:",node_d[path[f+2]]["y"]) + #print("node data:",node_d) + #f+1 deki noktayı path den çıkartıp araya yeni noktaları yerleştirmemlazım + #burda pathde keskin donuşte f+1 de olan kose noktasını path den çıkarıp yeni noktaları edge olarak araya ekliycem + #print("path::::",path) + #print("koşe noktası ::::",path[f+1],"index:",path.index(path[f+1])) + new_path.remove(path[f+1])#path de koşe noktasını new path densilicem + + #şimdi yeni noktaları path e sırasıyla insert edicem + temp_index=path.index(path[f+1]) + #print("bunun peşine ekle :",path[f+1]) + for insrt in temp_new_nodelist: + new_path.insert(temp_index,insrt) + temp_index=temp_index+1 + #indexde hata var sabah bak 93 deden sonrası path de çakışma olmuş + #print("new:path :",new_path) + #edge + ####3 self.plotbfmc(new_node_data,new_path) + #print("new path :",new_path)#en son path + source_target=[] + pathlen=len(new_path) + #print("len :",pathlen) + for n_edge in range(pathlen-1): + source_target.append((new_path[n_edge],new_path[n_edge+1],True)) + #print("formattli:",source_target) + #print("--------------------------------------------") + #print("node data:",new_node_data) + print("leaving here1") + + self.plotbfmc(new_node_data,new_path) + + + print("leaving here2") + return new_node_data,source_target + + #edge haline getirip return edicem + + #print("new_node_data new new :",new_node_data) + + #beizer2:3 noktayı bulunca beizer ile ara noktalar oluşturup path de araya inssert edicem + + #formatlama:düğüm düğüm olan path i edge edge hale gertip return edicem + + def extract_edges_data(self, root): + edges_data = [] + edges_data_true = [] + for edge in root.findall(".//{http://graphml.graphdrawing.org/xmlns}edge"): + source_id = edge.get('source') + target_id = edge.get('target') + data_d2 = edge.find(".//{http://graphml.graphdrawing.org/xmlns}data[@key='d2']") + d2_value = data_d2 is not None and data_d2.text == 'True' + # None null safety gibi bişey mi sor + source_id_int = int(source_id) + target_id_int = int(target_id) + ranges = [(469, 479), (409, 425), (386, 398), (357, 365), (259, 281)] + #ranges = [(469, 479)] + if any(start <= source_id_int <= end for start, end in ranges) and any(start <= target_id_int <= end for start, end in ranges): + d2_value = True + #d2 dokumantasyonda solanabilir yerleri belirtmek için kullanılmış ama burda devam belirteci olarak kullanılıyo bunu sor + edges_data_true.append((source_id, target_id, d2_value)) + edges_data.append((source_id, target_id, d2_value)) + #edeges_data normal dutun edgelerin verisi edges_data_true ranges dahilindeki true olan edgeler + return edges_data, edges_data_true + +############################################newpathfinding + + def obstacleDetector_callback(self, data): + # print("obstacleDetector_callback") + self.center_x = [] + self.center_y = [] + if not data.circles: # Eğer circles dizisi boşsa + # Direkt olarak 0.0, 0.0 ekleyin + # self.center_x.append(0.0) + # self.center_y.append(0.0) + pass + else: + # Eğer circles boş değilse, her bir dairenin merkez koordinatlarını al ve listeye ekle + for circle in data.circles: + self.center_x.append(circle.center.x) + self.center_y.append(circle.center.y) + + #Debug Start + def debug_callback(self): + current_time = rospy.Time.now().to_sec() + PassedTime = current_time - self.prev_time + # rospy.loginfo("Elapsed time: %.4f seconds", PassedTime) + self.prev_time = current_time + # print("self.nodes_data",self.nodes_data) + # print("self.edges_data",self.edges_data) + # print("self.edges_data_true",self.edges_data_true) + # print("self.SourceTargetNodes",self.SourceTargetNodes) + # print("self.obs_dict",self.obs_dict) + # print("self.pathGoalsYawDegree",self.pathGoalsYawDegree) + # print("self.path",self.path ) + + def pose_callback(self, data): + + start_time = time.time() + # print("Position: ", data.pose.position) + # print("Orientation: ", data.pose.orientation) + self.position_x = data.pose.position.x + self.position_y = data.pose.position.y + # print("self.position_x",self.position_x) + # print("self.position_y",self.position_y) + + quaternion = (data.pose.orientation.x,data.pose.orientation.y,data.pose.orientation.z, data.pose.orientation.w) + (self.roll, self.pitch, yaw_rad) = euler_from_quaternion(quaternion) + self.yaw_rad = yaw_rad + end_time = time.time() + duration = end_time - start_time # İşlemin sürdüğü süreyi hesapla + # print(f"CallbackPosition süresi: {duration} saniye.") + self.localisation_CB = True + self.IMU_cb = True + #self.mpcTimerCallback(event=None) + + def carControlPublisher(self): + + pass + # # print("carcontroltest") + # ser = serial.Serial(port='/dev/ttyACM0',baudrate=19200) + # #switchOn=input('Hiz Degerini Giriniz' + # # print("carcontroltest2") + # switchOn = "#1:{};;\r\n".format(self.steerLateral*100) + + # ser.write(bytes(switchOn,'utf-8')) + # # value=ser.readline() + # # valueInString=str(value,'UTF-8') + # # print(valueInString) + + # if self.steerAngle > 25: + # self.steerAngle = 25 + # if self.steerAngle < -25: + # self.steerAngle = -25 + + # #switchOn=input('Hiz Degerini Giriniz') + # switchOn = "#2:{};;\r\n".format(-1*(self.steerAngle)) + # ser.write(bytes(switchOn,'utf-8')) + + # rospy.loginfo("used steeringAngle %s", self.steerAngle) + # rospy.loginfo("used steeringLateral %s", self.steerLateral*100) + + # value=ser.readline() + # valueInString=str(value,'UTF-8') + #print(valueInString) + + # print("self.steerAngle",self.steerAngle) + # print("self.steerLateral",self.steerLateral*100) + + # carData = {} + # carData = { + # 'action': '2', + # 'steerAngle': self.steering_angle + # # 'steerAngle': 30.0 + # } + # # print("data",carData) + # # print("math.degrees(float(self.DM2Arr(self.u[0, 1]))) ",math.degrees(float(self.DM2Arr(self.u[0, 1]))) ) + # self.carData = json.dumps(carData) + # self.carControl.publish(self.carData) + # #rospy.sleep(0.01) + # car2Data = {} + # # İkinci mesaj için veriler + # car2Data = { + # 'action': '1', + # 'speed': self.steerLateral + # #'speed': 0.0 + # } + # #print("data",car2Data) + # self.car2Data = json.dumps(car2Data) + # self.carControl.publish(self.car2Data) + + + +def main(): + rospy.init_node('MPCtracking', anonymous=False) + cameratracker = mpc(camera_topic_name = "/zed2i/zed_node/rgb/image_rect_color", + pose_topic_name = "/zed2i/zed_node/pose") + + rate = rospy.Rate(200) # 10Hz + try: + while not rospy.is_shutdown(): + # Sürekli çalışacak kodlar burada + rate.sleep() + except KeyboardInterrupt: + cv2.destroyAllWindows() + + +if __name__ == '__main__': + main() diff --git a/src/example/src/tensorrtyolo.py b/src/example/src/tensorrtyolo.py new file mode 100644 index 0000000..7c6c289 --- /dev/null +++ b/src/example/src/tensorrtyolo.py @@ -0,0 +1,128 @@ +import torch +import cv2 +import random +import time +import pathlib +from ultralytics import YOLO + +import modules.utils as utils +from modules.autobackend import AutoBackend + + +def tensorrt_detection(model, source, image): + # Preprocess + im = utils.preprocess(image) + + # Inference + preds = model(im) + + # Post Process + results = utils.postprocess(preds, im, image, model.names, source) + d = results[0].boxes + + # Get information from result + tensor_size = d.cls.size()[0] + if (tensor_size > 1): + cls, conf, box = d.cls.squeeze(), d.conf.squeeze(), d.xyxy.squeeze() + else: + cls, conf, box = d.cls, d.conf, d.xyxy + + return cls, conf, box + + +def yolov8_detection(model, image): + # Update object localizer + results = model.predict(image, imgsz=640, conf=0.5, verbose=False) + result = results[0].cpu() + + # Get information from result + box = result.boxes.xyxy.numpy() + conf = result.boxes.conf.numpy() + cls = result.boxes.cls.numpy().astype(int) + + return cls, conf, box + + +def detection(model_path, source, name): + # Check File Extension + file_extension = pathlib.Path(model_path).suffix + + if (file_extension == ".engine"): + model = AutoBackend(model_path, device=torch.device('cuda:0'), fp16=True) + # Warmup + model.warmup() + else: + model = YOLO(model_path) + + # Class Name and Colors + label_map = model.names + COLORS = [[random.randint(0, 255) for _ in range(3)] for _ in label_map] + + # FPS Detection + frame_count = 0 + total_fps = 0 + avg_fps = 0 + + # FPS Video + video_cap = cv2.VideoCapture(source) + + total_frames = int(video_cap.get(cv2.CAP_PROP_FRAME_COUNT)) + frame_width = int(video_cap.get(3)) + frame_height = int(video_cap.get(4)) + + video_frames = [] + + while video_cap.isOpened(): + ret, frame = video_cap.read() + if not ret: + break + + # # Start Time + start = time.time() + + # Detection + if (file_extension == ".engine"): + cls, conf, box = tensorrt_detection(model, source, frame) + else: + cls, conf, box = yolov8_detection(model, frame) + + # Pack together for easy use + detection_output = list(zip(cls, conf, box)) + image_output = utils.draw_box(frame, detection_output, label_map, COLORS) + + end = time.time() + # # End Time + + # Draw FPS + frame_count += 1 + fps = 1 / (end - start) + total_fps = total_fps + fps + avg_fps = total_fps / frame_count + + image_output = utils.draw_fps(avg_fps, image_output) + + # Append frame to array + video_frames.append(image_output) + + # + print("(%2d / %2d) Frames Processed" % (frame_count, total_frames)) + + print(avg_fps) + + # Get a file name + file_name = utils.get_name(source) + # Get Save Path + folder_name = name + save_path = utils.get_save_path(file_name, folder_name) + # Create VideoWriter object. + out = cv2.VideoWriter(save_path, cv2.VideoWriter_fourcc(*'XVID'), int(avg_fps), (frame_width, frame_height)) + + for frame in video_frames: + out.write(frame) + + out.release() + + print("Video is saved in: " + save_path) + + +detection("besLevhaS2.engine", "video.mp4", "detection") \ No newline at end of file diff --git a/src/example/src/tfpublisher.py b/src/example/src/tfpublisher.py new file mode 100755 index 0000000..2988358 --- /dev/null +++ b/src/example/src/tfpublisher.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 +# coding: utf-8 + +import rospy +import tf2_ros +import geometry_msgs.msg +from gazebo_msgs.msg import LinkStates + +br = tf2_ros.TransformBroadcaster() + +# def publish_fixed_transform(): +# t = geometry_msgs.msg.TransformStamped() +# t.header.stamp = rospy.Time.now() +# t.header.frame_id = "automobile_chassis_link" +# t.child_frame_id = "automobile_laser_frame" +# t.transform.translation.x = 0.0 +# t.transform.translation.y = -0.22 +# t.transform.translation.z = -0.005 +# # Sabit dönüşüm için quaternion rpy'den (0, 0, 0) dönüştürülür +# t.transform.rotation.x = 0 +# t.transform.rotation.y = 0 +# t.transform.rotation.z = 0 +# t.transform.rotation.w = 1 + +# br.sendTransform(t) + +def link_states_callback(msg): + t = geometry_msgs.msg.TransformStamped() + + # publish_fixed_transform() + + for i in range(len(msg.name)): + if "automobile::chassis::link" in msg.name[i]: + t.header.stamp = rospy.Time.now() + rospy.Duration(0, 100000000*(i+1)) + t.header.frame_id = "map" # Odometri çerçevesinden + t.child_frame_id = "automobile_chassis_link" # aracın şasisine transform + # Pose verilerini al + t.transform.translation.x = msg.pose[i].position.x + t.transform.translation.y = msg.pose[i].position.y + t.transform.translation.z = msg.pose[i].position.z + t.transform.rotation.x = msg.pose[i].orientation.x + t.transform.rotation.y = msg.pose[i].orientation.y + t.transform.rotation.z = msg.pose[i].orientation.z + t.transform.rotation.w = msg.pose[i].orientation.w + + br.sendTransform(t) + +if __name__ == '__main__': + try: + rospy.init_node('gazebo_link_states_to_tf', anonymous=True) + rospy.set_param('/use_sim_time', True) + rospy.Subscriber('/gazebo/link_states', LinkStates, link_states_callback) + rate = rospy.Rate(10.0) + while not rospy.is_shutdown(): + rate.sleep() + except rospy.ROSInterruptException: + pass + diff --git a/src/example/src/yolo.py b/src/example/src/yolo.py new file mode 100755 index 0000000..3b92354 --- /dev/null +++ b/src/example/src/yolo.py @@ -0,0 +1,103 @@ +#!/usr/bin/env python3 + +import rospy +import cv2 +import numpy as np +from sensor_msgs.msg import Image +from cv_bridge import CvBridge, CvBridgeError +import math +from geometry_msgs.msg import Twist +from std_msgs.msg import String +import json +import supervision as sv +from time import time +from ultralytics import YOLO +import torch + +from example.msg import MekatronomYolo + +class yolo(): + def __init__(self): + self.bridge = CvBridge() + self.image_sub = rospy.Subscriber("/automobile/image_raw",Image,self.image_callback) + self.yoloPub = rospy.Publisher("/automobile/yolo", MekatronomYolo, queue_size=10) + + self.device = 'cuda' if torch.cuda.is_available() else 'cpu' + print("Using Device: ", self.device) + + self.model = YOLO("/home/mekatronom/boschmekatronom_ws/BoschMekatronom/src/weights/topluLevhaM100Epoch.pt") + self.image_callback_flag = False + rospy.Timer(rospy.Duration(0.5), self.set_callback) + + + def plot_detections(self,frame, class_name, top, left, bottom, right): + # Draw rectangle around the detection + cv2.rectangle(frame, (left, top), (right, bottom), (0, 255, 0), 2) + + # Prepare label text with class name and confidence + label_text = f'{class_name}' + + # Put label text on the image + cv2.putText(frame, label_text, (left, top - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2) + + return frame + + def yolo_callback(self): + model_input_size = (480, 640) # Example: (height, width) that model expects + scale_x = self.frame.shape[1] / model_input_size[1] + scale_y = self.frame.shape[0] / model_input_size[0] + print("Scaling factors:", scale_x, scale_y) + + # Initialize frame_with_detections with the original frame + # Ensures there is always a frame to display even if no detections occur + frame_with_detections = self.frame.copy() + + results = self.model(self.frame) + + for r in results: + boxes = r.boxes + for box in boxes: + b = box.xyxy[0].to('cpu').detach().numpy().copy() + c = box.cls + class_name = self.model.names[int(c)] + top = int(b[1] * scale_y) + left = int(b[0] * scale_x) + bottom = int(b[3] * scale_y) + right = int(b[2] * scale_x) + + rospy.loginfo(f"Class: {class_name} Top: {top} Left: {left} Bottom: {bottom} Right: {right}") + # Update frame_with_detections with each detection + frame_with_detections = self.plot_detections(frame_with_detections, class_name, top, left, bottom, right) + + cv2.imshow('color', frame_with_detections) + cv2.waitKey(1) + + def set_callback(self, event): + + self.yolo_callback() + + + def image_callback(self, data): + try: + self.frame = self.bridge.imgmsg_to_cv2(data, "bgr8") + self.image_callback_flag = True + + except CvBridgeError as e: + print(e) + + + +def main(): + rospy.init_node('yolo', anonymous=False) + + yl = yolo() + rate = rospy.Rate(3) + try: + while not rospy.is_shutdown(): + # Sürekli çalışacak kodlar burada + rate.sleep() + except KeyboardInterrupt: + cv2.destroyAllWindows() + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/src/example/src/yolo_intersection.py b/src/example/src/yolo_intersection.py new file mode 100755 index 0000000..1d78869 --- /dev/null +++ b/src/example/src/yolo_intersection.py @@ -0,0 +1,323 @@ +#!/usr/bin/env python3 + +import rospy +import cv2 +import numpy as np +from sensor_msgs.msg import Image +from cv_bridge import CvBridge, CvBridgeError +import math +from geometry_msgs.msg import Twist +from std_msgs.msg import String, Bool +import json +#import supervision as sv +import time +from ultralytics import YOLO +import torch +import serial +#import pyzed.sl as sl +import sys + +from example.msg import MekatronomYolo + +class yolo(): + def __init__(self): + + self.stop_flag = False + self.stop_flag2 = False + self.mesafe = 15.0 + + self.bridge = CvBridge() + + #Subscribers + self.image_sub = rospy.Subscriber("/automobile/image_raw",Image,self.image_callback) + + #Publishers + self.pub = rospy.Publisher("/automobile/intersection", Bool, queue_size=10) + self.yoloPub = rospy.Publisher("/automobile/yolo", MekatronomYolo, queue_size=10) + + + #For checking intersection time + self.last_update_time = rospy.Time.now() + self.last_path_index = 0 + self.last_target_update_time = None + self.current_target = None + + + self.device = 'cuda' if torch.cuda.is_available() else 'cpu' + print("Using Device: ", self.device) + + self.model = YOLO("/home/mekatronom/boschmekatronom_ws/BoschMekatronom/src/weights/bosch.pt") + self.image_callback_flag = False + rospy.Timer(rospy.Duration(0.6), self.set_callback) + #rospy.Timer(rospy.Duration(0.6), self.intersection_callback(self.frame)) + + self.control=0 + self.class_name = "" + + #self.intersection_callback(self.frame.copy()) + + + def plot_detections(self,frame, class_name, top, left, bottom, right): + # Draw rectangle around the detection + cv2.rectangle(frame, (left, top), (right, bottom), (0, 255, 0), 2) + + # Prepare label text with class name and confidence + label_text = f'{class_name}' + + # Put label text on the image + cv2.putText(frame, label_text, (left, top - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2) + + return frame + + def yolo_callback(self): + self.yolomessage = MekatronomYolo() + + model_input_size = (480, 640) # Example: (height, width) that model expects + scale_x = self.frame.shape[1] / model_input_size[1] + scale_y = self.frame.shape[0] / model_input_size[0] + print("Scaling factors:", scale_x, scale_y) + + # Initialize frame_with_detections with the original frame + # Ensures there is always a frame to display even if no detections occur + frame_with_detections = self.frame.copy() + print("test") + results = self.model(self.frame) + print("test") + for r in results: + boxes = r.boxes + print("test") + for box in boxes: + print("test") + b = box.xyxy[0].to('cpu').detach().numpy().copy() + c = box.cls + self.class_name = self.model.names[int(c)] + top = int(b[1] * scale_y) + left = int(b[0] * scale_x) + bottom = int(b[3] * scale_y) + right = int(b[2] * scale_x) + label_text = f'{self.class_name}' + + self.yolomessage.object.append(label_text) + + rospy.loginfo(f"Class: {self.class_name} Top: {top} Left: {left} Bottom: {bottom} Right: {right}") + # Update frame_with_detections with each detection + frame_with_detections = self.plot_detections(frame_with_detections, self.class_name, top, left, bottom, right) + + + self.intersection_callback(self.frame.copy()) + + self.yolomessage.distance = [float(self.mesafe)] + + self.yoloPub.publish(self.yolomessage) + + #self.control_callback(label_text) + + cv2.imshow('color', frame_with_detections) + cv2.waitKey(1) + + + def control_callback(self, label_text): + + + if label_text == "stop" or label_text == "crosswalk": + self.stop_flag = True + self.stop_flag2 = True + if self.bosluk == 0 and 3 20: + self.steering_angle = 20 + if self.steering_angle < -20: + self.steering_angle = -20 + + ser = serial.Serial(port='/dev/ttyACM0',baudrate=19200) + #switchOn=input('Hiz Degerini Giriniz') + print("Steering Angle: ", self.steering_angle) + switchOn = f"#2:{self.speed.angular.z * 10};;\r\n" + ser.write(bytes(switchOn,'utf-8')) + value=ser.readline() + valueInString=str(value,'utf-8') + + + +def main(): + rospy.init_node('yolo', anonymous=False) + + yl = yolo() + rate = rospy.Rate(15) + try: + while not rospy.is_shutdown(): + # Sürekli çalışacak kodlar burada + rate.sleep() + except KeyboardInterrupt: + cv2.destroyAllWindows() + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/src/example/src/yolo_intersection_real.py b/src/example/src/yolo_intersection_real.py new file mode 100755 index 0000000..f658f57 --- /dev/null +++ b/src/example/src/yolo_intersection_real.py @@ -0,0 +1,291 @@ +#!/usr/bin/env python3 + +import rospy +import cv2 +import numpy as np +from sensor_msgs.msg import Image +from cv_bridge import CvBridge, CvBridgeError +import math +from geometry_msgs.msg import Twist +from std_msgs.msg import String +import json +#import supervision as sv +import time +from ultralytics import YOLO +import torch +import serial +#import pyzed.sl as sl +import sys + +from example.msg import MekatronomYolo + + +class yolo(): + def __init__(self): + + self.stop_flag = False + self.stop_flag2 = False + self.mesafe = 15 + + self.bridge = CvBridge() + #self.image_sub = rospy.Subscriber("/camera/rgb/image_raw",Image,self.image_callback) + + #Subscribers + self.image_sub = rospy.Subscriber("/zed2i/zed_node/rgb/image_rect_color",Image,self.image_callback) + self.depth_sub = rospy.Subscriber("/zed2i/zed_node/depth/depth_registered",Image,self.depth_callback) + #Publishers + self.yoloPub = rospy.Publisher("/automobile/yolo", MekatronomYolo, queue_size=1) + + self.device = 'cuda' if torch.cuda.is_available() else 'cpu' + print("Using Device: ", self.device) + + self.model = YOLO("/home/mekatronom/boschmekatronom_ws/BoschMekatronom/src/weights/boschM100Epoch.pt") + self.image_callback_flag = False + rospy.Timer(rospy.Duration(0.5), self.set_callback) + + self.control=0 + self.class_name = "" + + #self.intersection_callback(self.frame.copy()) + + + def plot_detections(self,frame, class_name, top, left, bottom, right): + # Draw rectangle around the detection + cv2.rectangle(frame, (left, top), (right, bottom), (0, 255, 0), 2) + + # Prepare label text with class name and confidence + label_text = f'{class_name}' + + # Put label text on the image + cv2.putText(frame, label_text, (left, top - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2) + + return frame + + def yolo_callback(self): + + self.yolomessage = MekatronomYolo() + + model_input_size = (360, 640) # Example: (height, width) that model expects + + scale_x = self.frame.shape[1] / model_input_size[1] + scale_y = self.frame.shape[0] / model_input_size[0] + # print("Scaling factors:", scale_x, scale_y) + # Initialize frame_with_detections with the original frame + # Ensures there is always a frame to display even if no detections occur + frame_with_detections = self.frame.copy() + # print("test") + results = self.model(self.frame) + # print("test") + for r in results: + boxes = r.boxes + # print("test") + for box in boxes: + # print("test") + b = box.xyxy[0].to('cpu').detach().numpy().copy() + c = box.cls + self.class_name = self.model.names[int(c)] + top = int(b[1] * scale_y) + left = int(b[0] * scale_x) + bottom = int(b[3] * scale_y) + right = int(b[2] * scale_x) + label_text = f'{self.class_name}' + + self.yolomessage.object.append(label_text) + rospy.loginfo(f"Class: {self.class_name} Top: {top} Left: {left} Bottom: {bottom} Right: {right}") + # Update frame_with_detections with each detection + frame_with_detections = self.plot_detections(frame_with_detections, self.class_name, top, left, bottom, right) + + self.intersection_callback(self.frame.copy()) + + self.yolomessage.distance = [float(self.mesafe)] + + self.yoloPub.publish(self.yolomessage) + + + + #cv2.rectangle(frame_with_detections, (right_top_x, right_top_y), (left_top_x, left_top_y),(255,0,255), 2) + + cv2.imshow("frame_with_detections", frame_with_detections) + cv2.waitKey(1) + + + def set_callback(self, event): + if self.image_callback_flag: + self.yolo_callback() + + + def image_callback(self, data): + try: + self.frame = self.bridge.imgmsg_to_cv2(data, "bgr8") + print("frame shape:", self.frame.shape) + + #self.frame = data#pyzedden data resim olarak geliyo mesaj olarak değil + self.image_callback_flag = True + + except CvBridgeError as e: + print(e) + pass + + def depth_callback(self, data): + try: + self.depth = self.bridge.imgmsg_to_cv2(data, "32FC1") + depths = np.frombuffer(data.data, dtype=np.float32).reshape(data.height, data.width) + self.depth_callback_flag = True + # Image coordinates of the center pixel + u = data.width // 2 + v = data.height // 2 + right_top_x, right_top_y = 125, 50 + left_top_x, left_top_y = 225, 150 + scan_res = 5 + + points_x = np.linspace(right_top_x, left_top_x, scan_res) + points_y = np.linspace(right_top_y, left_top_y, scan_res) + depth_values = [] + for x in points_x: + for y in points_y: + depth = depths[int(y), int(x)] + if 0.3 < depth: + depth_values.append(depth) + + # Access the depth value of the center pixel + # center_depth = depths[v, u] + + except CvBridgeError as e: + # print(e) + pass + + + def intersection_callback(self, frame): + # print("girdi") + img = cv2.resize(frame, (640,480)) + gray_image = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) + hue_min = 0#0 + hue_max = 60#60 + sat_min = 0#0 + sat_max = 255#255 + val_min = 70#70 + val_max = 255#255 + + # HSV değerlerine dayalı maske oluşturun + lower_bound = np.array([hue_min, sat_min, val_min]) + upper_bound = np.array([hue_max, sat_max, val_max]) + # mask = cv2.inRange(hsv_img, lower_bound, upper_bound) + + imgBlur= cv2.GaussianBlur(img, (7, 7), 1) + + imgHSV= cv2.cvtColor(imgBlur, cv2.COLOR_BGR2HSV) + + imgMask = cv2.inRange(gray_image, 200, 255) + + kernel = np.ones((5, 5)) + imgDil = cv2.dilate(imgMask, kernel, iterations=2) + + imgContour = imgDil.copy() + + width, height = 640, 480 + + bottom_right = [990, 480] # 640, 480 + bottom_left = [-350, 480] # 0, 480 + top_right = [600, 240] + top_left = [40, 240] + + src = np.float32([top_left, top_right, bottom_left, bottom_right]) + + # Dönüşüm sonucu alınacak köşe noktalarını belirleme + dst = np.float32([[0, 0], [width, 0], [0, height], [width, height]]) + matrix = cv2.getPerspectiveTransform(src, dst) + imgWarp = cv2.warpPerspective(img, matrix, (width, height)) + + imgContourPers = imgWarp.copy() + gray_image = cv2.cvtColor(imgContourPers, cv2.COLOR_BGR2GRAY) + imgMaskGray = cv2.inRange(gray_image, 230, 255) + imgHSVPers = cv2.cvtColor(imgWarp, cv2.COLOR_BGR2HSV) + imgMaskPers = cv2.inRange(imgHSVPers, lower_bound, upper_bound) + + imgDilPers = cv2.dilate(imgMaskGray, kernel, iterations=2) + + #cv2.imshow("perspective:",imgDilPers) + kernel2= np.ones((1,20),np.uint8) + y=10 + x=200 + h=460 + w=240 + crop = imgDilPers[y:y+h, x:x+w]# bu crop u sıfır matrisinde yerine geri koymam lazım + # cv2.imshow("croped",crop) + #bos = np.zeros((480, 640))# bunu içine cropu konumuna insert edicem + bos=np.zeros((480, 640, 3), dtype = np.uint8) + bosx= cv2.cvtColor(bos, cv2.COLOR_BGR2GRAY) + + bosx[10:10+460, 200:200+240] = crop + # cv2.imshow("vvvvvvvvvvvvvv",bosx) + #roi nin ayarlanıp kırpılıp yenşiden genişletilmiş hali + + yatayline= cv2.erode(bosx,kernel2,iterations=3)#erode çakıştırma and alarak filitreleme + + self.bosluk=np.all(yatayline==0) #bosluk = 0 gelirse duz cizgi detected + if not self.bosluk: + + # print("düz çizgi var ") + backtorgb = cv2.cvtColor(yatayline,cv2.COLOR_GRAY2RGB) + backtorgb2 = cv2.cvtColor(imgDilPers,cv2.COLOR_GRAY2RGB) + # cv2.imshow("bactorgb",backtorgb2) + lower= np.array([200, 200, 200], dtype = "uint8") + upper= np.array([255, 255, 255], dtype = "uint8") + mask = cv2.inRange(backtorgb,lower,upper) + backtorgb[np.where((backtorgb==[255,255,255]).all(axis=2))] = [0,0,255] + boyali =cv2.addWeighted(backtorgb2, 0.5, backtorgb, 0.7, 0) + #255 128 128 BOYA + #128 128 128 YOL + # cv2.imshow("rgb :",backtorgb) + # cv2.imshow("hedef :",boyali) + + gren=[0,255,0] + renk= np.array([0,0,255]) + ust=0 + alt=0 + + for i in np.arange(480): + a=backtorgb[i][325] + if np.array_equal(a, renk): + backtorgb = cv2.circle(backtorgb, (325,i), 2, gren, -1) + #print("buldum ") + ust=i + break + for i in np.arange(480): + reverse=479-i + #print("i:",reverse) + a=backtorgb[reverse][325] + if np.array_equal(a, renk): + backtorgb = cv2.circle(backtorgb, (325,reverse), 2, gren, -1) + #print("buldum ") + alt=reverse + break + #print("ust :",ust,"alt",alt) + if alt==ust: + ust=ust+5 + self.mesafe=((480-alt)/abs(alt-ust))*4 + # print("mesafe:",self.mesafe)# + + # cv2.imshow("rgb :",backtorgb) + # cv2.imshow("hedef :",boyali) + + + + # cv2.imshow("original:",imgDil) + # cv2.imshow("kontrol",yatayline) + #cv2.waitKey(1) + +def main(): + rospy.init_node('yolo', anonymous=False) + + yl = yolo() + rate = rospy.Rate(10) + try: + while not rospy.is_shutdown(): + # Sürekli çalışacak kodlar burada + rate.sleep() + except KeyboardInterrupt: + cv2.destroyAllWindows() + +if __name__ == '__main__': + main() diff --git a/src/models_pkg/cam_birdeye_REC/model.sdf b/src/models_pkg/cam_birdeye_REC/model.sdf index 4a3c8f3..7a9f4ba 100644 --- a/src/models_pkg/cam_birdeye_REC/model.sdf +++ b/src/models_pkg/cam_birdeye_REC/model.sdf @@ -132,7 +132,7 @@ 0.0 0.0 0.0 - + automobile diff --git a/src/models_pkg/camera/model.sdf b/src/models_pkg/camera/model.sdf index 1d00786..af7f003 100644 --- a/src/models_pkg/camera/model.sdf +++ b/src/models_pkg/camera/model.sdf @@ -131,7 +131,7 @@ 0.0 0.0 0.0 - + automobile diff --git a/src/models_pkg/camera_follow/model.sdf b/src/models_pkg/camera_follow/model.sdf index 21d0796..20a309f 100644 --- a/src/models_pkg/camera_follow/model.sdf +++ b/src/models_pkg/camera_follow/model.sdf @@ -132,7 +132,7 @@ 0.0 0.0 0.0 - + automobile diff --git a/src/models_pkg/car_body/model.sdf b/src/models_pkg/car_body/model.sdf index ed067c2..db4d551 100644 --- a/src/models_pkg/car_body/model.sdf +++ b/src/models_pkg/car_body/model.sdf @@ -1,6 +1,6 @@ - + 1 diff --git a/src/models_pkg/chassis/model.sdf b/src/models_pkg/chassis/model.sdf index 927d644..fa0af53 100644 --- a/src/models_pkg/chassis/model.sdf +++ b/src/models_pkg/chassis/model.sdf @@ -14,12 +14,12 @@ 0 0 0 0 -0 0 - 0 0 0 0 0 1.5708 + 0 0 0 0 0 0.0 1 0 0 - 0 0 -0.0065 0 0 -1.57079 + 0 0 -0.0065 0 0 0.0 model://chassis/meshes/chassisV5.dae diff --git a/src/models_pkg/lidar/lidar.xacro b/src/models_pkg/lidar/lidar.xacro new file mode 100644 index 0000000..e9bc59e --- /dev/null +++ b/src/models_pkg/lidar/lidar.xacro @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Gazebo/Red + + + 0 0 0 0 0 0 + true + 10 + + + + 360 + -3.14 + 3.14 + + + + 0.3 + 12 + + + + + ~/out:=scan + + sensor_msgs/LaserScan + laser_frame + + + + + \ No newline at end of file diff --git a/src/models_pkg/rcCar_assembly/model.sdf b/src/models_pkg/rcCar_assembly/model.sdf index 12d4b03..1c9c800 100644 --- a/src/models_pkg/rcCar_assembly/model.sdf +++ b/src/models_pkg/rcCar_assembly/model.sdf @@ -309,13 +309,7 @@ - - - chassis::link - lidar::lidar_link - 0 0 0 0 -0 0 - - + 0.26 0.162 diff --git a/src/models_pkg/rcCar_assembly/model.urdf b/src/models_pkg/rcCar_assembly/model.urdf index 783c7e8..00f4bfc 100644 --- a/src/models_pkg/rcCar_assembly/model.urdf +++ b/src/models_pkg/rcCar_assembly/model.urdf @@ -5,28 +5,28 @@ - + - + - + - + @@ -60,7 +60,7 @@ - + diff --git a/src/models_pkg/rcCar_assembly/test.urdf b/src/models_pkg/rcCar_assembly/test.urdf new file mode 100644 index 0000000..a2a5d2f --- /dev/null +++ b/src/models_pkg/rcCar_assembly/test.urdf @@ -0,0 +1,244 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/models_pkg/rcCar_assembly_REC/model.sdf b/src/models_pkg/rcCar_assembly_REC/model.sdf index 2fe2e4c..3006377 100644 --- a/src/models_pkg/rcCar_assembly_REC/model.sdf +++ b/src/models_pkg/rcCar_assembly_REC/model.sdf @@ -385,7 +385,7 @@ 0.0 0.0 - / + automobile diff --git a/src/obstacle_detector/CMakeLists.txt b/src/obstacle_detector/CMakeLists.txt new file mode 100644 index 0000000..d2d2ba1 --- /dev/null +++ b/src/obstacle_detector/CMakeLists.txt @@ -0,0 +1,130 @@ +cmake_minimum_required(VERSION 2.8.3) +project(obstacle_detector) + +set(CMAKE_CXX_FLAGS "-std=c++11 -fpermissive ${CMAKE_CXX_FLAGS} -Wfatal-errors\ ") + +find_package(catkin REQUIRED COMPONENTS roscpp roslaunch nodelet rviz std_msgs std_srvs geometry_msgs sensor_msgs tf laser_geometry message_generation) +find_package(Armadillo REQUIRED) +find_package(Boost 1.54.0 REQUIRED system) + +add_message_files(FILES CircleObstacle.msg SegmentObstacle.msg Obstacles.msg) +generate_messages(DEPENDENCIES std_msgs geometry_msgs) + +catkin_package( + INCLUDE_DIRS include + LIBRARIES scans_merger obstacle_extractor obstacle_tracker obstacle_publisher ${PROJECT_NAME}_nodelets ${PROJECT_NAME}_gui + CATKIN_DEPENDS roscpp nodelet rviz std_msgs std_srvs geometry_msgs sensor_msgs tf laser_geometry message_runtime +) + +include_directories(include ${catkin_INCLUDE_DIRS} ${ARMADILLO_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}) + +# +# Build libs +# +add_library(scans_merger src/scans_merger.cpp) +target_link_libraries(scans_merger ${catkin_LIBRARIES}) +add_dependencies(scans_merger ${catkin_EXPORTED_TARGETS}) + +add_library(obstacle_extractor src/obstacle_extractor.cpp) +target_link_libraries(obstacle_extractor ${catkin_LIBRARIES} ${ARMADILLO_LIBRARIES}) +add_dependencies(obstacle_extractor ${catkin_EXPORTED_TARGETS} ${${PROJECT_NAME}_EXPORTED_TARGETS}) + +add_library(obstacle_tracker src/obstacle_tracker.cpp) +target_link_libraries(obstacle_tracker ${catkin_LIBRARIES} ${ARMADILLO_LIBRARIES}) +add_dependencies(obstacle_tracker ${catkin_EXPORTED_TARGETS} ${${PROJECT_NAME}_EXPORTED_TARGETS}) + +add_library(obstacle_publisher src/obstacle_publisher.cpp) +target_link_libraries(obstacle_publisher ${catkin_LIBRARIES}) +add_dependencies(obstacle_publisher ${catkin_EXPORTED_TARGETS} ${${PROJECT_NAME}_EXPORTED_TARGETS}) + +# +# Build nodes +# +add_executable(scans_merger_node src/nodes/scans_merger_node.cpp) +target_link_libraries(scans_merger_node scans_merger) + +add_executable(obstacle_extractor_node src/nodes/obstacle_extractor_node.cpp) +target_link_libraries(obstacle_extractor_node obstacle_extractor) + +add_executable(obstacle_tracker_node src/nodes/obstacle_tracker_node.cpp) +target_link_libraries(obstacle_tracker_node obstacle_tracker) + +add_executable(obstacle_publisher_node src/nodes/obstacle_publisher_node.cpp) +target_link_libraries(obstacle_publisher_node obstacle_publisher) + +# +# Build nodelets +# +add_library(${PROJECT_NAME}_nodelets + src/nodelets/scans_merger_nodelet.cpp + src/nodelets/obstacle_extractor_nodelet.cpp + src/nodelets/obstacle_tracker_nodelet.cpp + src/nodelets/obstacle_publisher_nodelet.cpp) +target_link_libraries(${PROJECT_NAME}_nodelets scans_merger obstacle_extractor obstacle_tracker obstacle_publisher) + +# +# Build rviz plugins +# +set(CMAKE_AUTOMOC ON) + +if(rviz_QT_VERSION VERSION_LESS "5") + message(STATUS "Using Qt4 based on the rviz_QT_VERSION: ${rviz_QT_VERSION}") + find_package(Qt4 ${rviz_QT_VERSION} EXACT REQUIRED QtCore QtGui) + include(${QT_USE_FILE}) +else() + message(STATUS "Using Qt5 based on the rviz_QT_VERSION: ${rviz_QT_VERSION}") + find_package(Qt5 ${rviz_QT_VERSION} EXACT REQUIRED Core Widgets) + set(QT_LIBRARIES Qt5::Widgets) +endif() + +add_definitions(-DQT_NO_KEYWORDS) + +add_library(${PROJECT_NAME}_gui + src/displays/obstacles_display.cpp include/${PROJECT_NAME}/displays/obstacles_display.h + src/displays/circle_visual.cpp include/${PROJECT_NAME}/displays/circle_visual.h + src/displays/segment_visual.cpp include/${PROJECT_NAME}/displays/segment_visual.h + # + src/panels/scans_merger_panel.cpp include/${PROJECT_NAME}/panels/scans_merger_panel.h + src/panels/obstacle_extractor_panel.cpp include/${PROJECT_NAME}/panels/obstacle_extractor_panel.h + src/panels/obstacle_tracker_panel.cpp include/${PROJECT_NAME}/panels/obstacle_tracker_panel.h + src/panels/obstacle_publisher_panel.cpp include/${PROJECT_NAME}/panels/obstacle_publisher_panel.h) +target_link_libraries(${PROJECT_NAME}_gui ${QT_LIBRARIES} ${catkin_LIBRARIES}) +add_dependencies(${PROJECT_NAME}_gui ${catkin_EXPORTED_TARGETS}) + +# +# Install libraries +# +install(TARGETS scans_merger obstacle_extractor obstacle_tracker obstacle_publisher ${PROJECT_NAME}_nodelets ${PROJECT_NAME}_gui + ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} + LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} + RUNTIME DESTINATION ${CATKIN_GLOBAL_BIN_DESTINATION}) + +# +# Install nodes +# +install(TARGETS scans_merger_node obstacle_extractor_node obstacle_tracker_node obstacle_publisher_node + RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}) + +# +# Install header files +# +install(DIRECTORY include/${PROJECT_NAME}/ + DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION}) + +# +# Install nodelet and rviz plugins description +# +install(FILES nodelet_plugins.xml rviz_plugins.xml + DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}) + +# +# Install launch files +# +install(DIRECTORY launch/ + DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}) + +# +# Create folders and copy resources +# +file(MAKE_DIRECTORY $ENV{HOME}/.local/share/icons/robor) +file(COPY resources/play.png resources/stop.png DESTINATION $ENV{HOME}/.local/share/icons/robor) diff --git a/src/obstacle_detector/README.md b/src/obstacle_detector/README.md new file mode 100644 index 0000000..ecc8cfb --- /dev/null +++ b/src/obstacle_detector/README.md @@ -0,0 +1,270 @@ +# The obstacle_detector package + +The `obstacle_detector` package provides utilities to detect and track obstacles from data provided by 2D laser scanners. Detected obstacles come in a form of line segments or circles. The package was designed for a robot equipped with two laser scanners therefore it contains several additional utilities. The working principles of the method are described in an article provided in the `resources` folder. + +The package requires [Armadillo C++](http://arma.sourceforge.net) library for compilation and runtime. + +----------------------- +

+ Visual example of obstacle detector output. +
+ Fig. 1. Visual example of obstacle detector output. +

+ +----------------------- + +1. The nodes and nodelets + 1.1 The scans_merger + 1.2 The obstacle_extractor + 1.3 The obstacle_tracker + 1.4 The obstacle_publisher +2. The messages +3. Launch files +4. The displays + +## 1. The nodes and nodelets + +The package provides separate nodes/nodelets to perform separate tasks. In general solution the data is processed in a following manner: + +`two laser scans` -> `scans merger` -> `merged scan or pcl` -> `obstacle extractor` -> `obstacles` -> `obstacle tracker` -> `refined obstacles` + +For some scenarios the pure obstacle extraction directly from a laser scan (without tracking) might be sufficient. + +The nodes are configurable with the use of ROS parameter server. All of the nodes provide a private `params` service, which allows the process to get the latest parameters from the parameter server. + +All of the nodes can be in either active or sleep mode, triggered by setting the appropriate variable in the parameter server and calling `params` service. In the sleep mode, any subscribers or publishers are shut down and the node does nothing until activated again. + +For the ease of use it is recomended to use appropriate Rviz panels provided for the nodes with the package. The Rviz panels communicate via parameter server and service-client calls, therefore the names of the nodes must be preserved unchanged (cf. launch files for examples). + +### 1.1. The scans_merger node + +This node converts two messages of type `sensor_msgs/LaserScan` from topics `front_scan` and `rear_scan` into a single laser scan of the same type, published under topic `scan` and/or a point cloud of type `sensor_msgs/PointCloud`, published under topic `pcl`. The difference between both is that the resulting laser scan divides the area into finite number of circular sectors and put one point (or actually one range value) in each section occupied by some measured points, whereas the resulting point cloud simply copies all of the points obtained from sensors. + +The input laser scans are firstly rectified to incorporate the motion of the scanner in time (see `laser_geometry` package). Next, two PCLs obtained from the previous step are synchronized and transformed into the target coordinate frame at the current point in time. + +----------------------- +

+ Visual example of scans_merger output +
+ Fig. 2. Visual example of `scans_merger` output. +

+ +----------------------- + +The resulting messages contain geometric data described with respect to a specific coordinate frame (e.g. `robot`). Assuming that the coordinate frames attached to two laser scanners are called `front_scanner` and `rear_scanner`, both transformation from `robot` frame to `front_scanner` frame and from `robot` frame to `rear_scanner` frame must be provided. The node allows to artificially restrict measured points to some rectangular region around the `robot` frame as well as to limit the resulting laser scan range. The points falling behind this region will be discarded. + +Even if only one laser scanner is used, the node can be useful for simple data pre-processing, e.g. rectification, range restriction or recalculation of points to a different coordinate frame. The node uses the following set of local parameters: + +* `~active` (`bool`, default: `true`) - active/sleep mode, +* `~publish_scan` (`bool`, default: `false`) - publish the merged laser scan message, +* `~publish_pcl` (`bool`, default: `true`) - publish the merged point cloud message, +* `~ranges_num` (`int`, default: `1000`) - number of ranges (circular sectors) contained in the 360 deg laser scan message, +* `~min_scanner_range` (`double`, default: `0.05`) - minimal allowable range value for produced laser scan message, +* `~max_scanner_range` (`double`, default: `10.0`) - maximal allowable range value for produced laser scan message, +* `~min_x_range` (`double`, default: `-10.0`) - limitation for points coordinates (points with coordinates behind these limitations will be discarded), +* `~max_x_range` (`double`, default: `10.0`) - as above, +* `~min_y_range` (`double`, default: `-10.0`) - as above, +* `~max_y_range` (`double`, default: `10.0`) - as above, +* `~fixed_frame_id` (`string`, default: `map`) - name of the fixed coordinate frame used for scan rectification in time, +* `~target_frame_id` (`string`, default: `map`) - name of the coordinate frame used as the origin for the produced laser scan or point cloud. + +In the original implementation, the `~target_frame_id` is the `robot` frame. However, with a moving robot, this causes the Kalman filters later on to think that the points are moving even though the robot is moving w. r. t. the map. That's why we enhance the point cloud with "range" (distance) information in the `scans_merger` node. This means that even though the origin of the target frame does not match the lidar scan origin, we still preserve the distance of each point to the scanner that produced the point. For that reason, it is absolutely essential to run the scans through the `scans_merger` node, even if not merging scans and to set `publish_pcl` to `true`. + +The package comes with Rviz panel for this node. + +----------------------- +

+ Rviz panel for the scans_merger node. +
+ Fig. 3. Rviz panel for the `scans_merger` node. +

+ +----------------------- + +### 1.2. The obstacle_extractor node + +This node converts messages of type `sensor_msgs/LaserScan` from topic `scan` or messages of type `sensor_msgs/PointCloud` from topic `pcl` into obstacles which are published as messages of custom type `obstacles_detector/Obstacles` under topic `raw_obstacles`. The PCL message must be ordered in the angular fashion, because the algorithm exploits the polar nature of laser scanners. + +----------------------- +

+ Visual example of obstacle_extractor output. +
+ Fig. 4. Visual example of `obstacle_extractor` output. +

+ +----------------------- + +The input points are firstly grouped into subsets and marked as visible or not (if a group is in front of neighbouring groups, it is visible. Otherwise it is assumed to be occluded). The algorithm extracts segments from each points subset. Next, the segments are checked for possible merging between each other. The circular obstacles are then extracted from segments and also merged if possible. Resulting set of obstacles can be transformed to a dedicated coordinate frame. + +The node is configurable with the following set of local parameters: + +* `~active` (`bool`, default: `true`) - active/sleep mode, +* `~use_scan` (`bool`, default: `false`) - use laser scan messages, +* `~use_pcl` (`bool`, default: `true`) - use point cloud messages (if both scan and pcl are chosen, scans will have priority), +* `~use_split_and_merge` (`bool`, default: `true`) - choose wether to use Iterative End Point Fit (false) or Split And Merge (true) algorithm to detect segments, +* `~circles_from_visibles` (`bool`, default: `true`) - detect circular obstacles only from fully visible (not occluded) segments, +* `~discard_converted_segments` (`bool`, default: `true`) - do not publish segments, from which the circles were spawned, +* `~transform_coordinates` (`bool`, default: `true`) - transform the coordinates of obstacles to a frame described with `frame_id` parameter, +* `~min_group_points` (`int`, default: `5`) - minimum number of points comprising a group to be further processed, +* `~max_group_distance` (`double`, default: `0.1`) - if the distance between two points is greater than this value, start a new group, +* `~distance_proportion` (`double`, default: `0.00628`) - enlarge the allowable distance between points proportionally to the range of point (use scan angle increment in radians), +* `~max_split_distance` (`double`, default: `0.2`) - if a point in group lays further from a leading line than this value, split the group, +* `~max_merge_separation` (`double`, default: `0.2`) - if distance between obstacles is smaller than this value, consider merging them, +* `~max_merge_spread` (`double`, default: `0.2`) - merge two segments if all of their extreme points lay closer to the leading line than this value, +* `~max_circle_radius` (`double`, default: `0.6`) - if a circle would have greater radius than this value, skip it, +* `~radius_enlargement` (`double`, default: `0.25`) - artificially enlarge the circles radius by this value, +* `~frame_id` (`string`, default: `map`) - name of the coordinate frame used as origin for produced obstacles (used only if `transform_coordinates` flag is set to true). + +The package comes with Rviz panel for this node. + +----------------------- +

+ Rviz panel for the obstacle_extractor node. +
+ Fig. 5. Rviz panel for the `obstacle_extractor` node. +

+ +----------------------- + +### 1.3. The obstacle_tracker node + +This node tracks and filters the circular and line segment obstacles with the use of Kalman filter. It subscribes to messages of custom type `obstacle_detector/Obstacles` from topic `raw_obstacles` and publishes messages of the same type under topic `tracked_obstacles`. The tracking algorithm is applied to both, the circular obstacles and line segments. The tracked obstacles are supplemented with additional information on their velocity. + +----------------------- +

+ Visual example of obstacle_tracker output. +
+ Fig. 6. Visual example of `obstacle_tracker` output. +

+ +----------------------- + +The node works in a synchronous manner with the default rate of 100 Hz. If detected obstacles are published less often, the tracker will supersample them and smoothen their position and radius. The following set of local parameters can be used to tune the node: + +* `~active` (`bool`, default: `true`) - active/sleep mode, +* `~copy_segments` (`bool`, default: `true`) - copy detected segments into tracked obstacles message, +* `~loop_rate` (`double`, default: `100.0`) - the main loop rate in Hz, +* `~tracking_duration` (`double`, default: `2.0`) - the duration of obstacle tracking in the case of lack of incomming data (after this time from the last corresponding measurement the tracked obstacle will be removed from the list), +* `~min_correspondence_cost` (`double`, default `0.3`) - a threshold for correspondence test, +* `~std_correspondence_dev` (`double`, default `0.15`) - (experimental) standard deviation of the position ellipse in the correspondence test, +* `~process_variance` (`double`, default `0.01`) - variance of obstacles position and radius (parameter of Kalman Filter), +* `~process_rate_variance` (`double`, default `0.1`) - variance of rate of change of obstacles values (parameter of Kalman Filter), +* `~measurement_variance` (`double`, default `1.0`) - variance of measured obstacles values (parameter of Kalman Filter), +* `~frame_id` (`string`, default: `map`) - name of the coordinate frame in which the obstacles are described, + +The package comes with Rviz panel for this node. + +----------------------- +

+ Rviz panel for the obstacle_tracker node. +
+ Fig. 7. Rviz panel for the `obstacle_tracker` node. +

+ +----------------------- + +### 1.4. The obstacle_publisher node + +The auxiliary node which allows to publish a set of virtual, circular obstacles in the form of message of type `obstacles_detector/Obstacles` under topic `obstacles`. The node is mostly used for off-line tests. The following parameters are used to configure the node: + +* `~active` (`bool`, default: `true`) - active/sleep mode, +* `~reset` (`bool`, default: `false`) - reset time for obstacles motion calculation (used by dedicated Rviz panel), +* `~fusion_example` (`bool`, default: `false`) - produce obstacles showing fusion, +* `~fission_example` (`bool`, default: `false`) - produce obstacles showing fission, +* `~radius_margin` (`double`, default: `0.25`) - artificially enlarge the circles radius by this value in meters, +* `~loop_rate` (`double`, default: `10.0`) - the main loop rate in Hz, +* `~frame_id` (`string`, default: `map`) - name of the coordinate frame in which the obstacles are described. + +The following parameters are used to provide the node with a set of obstacles: + +* `~x_vector` (`std::vector`, default: `[]`) - the array of x-coordinates of obstacles center points, +* `~y_vector` (`std::vector`, default: `[]`) - the array of y-coordinates of obstacles center points, +* `~r_vector` (`std::vector`, default: `[]`) - the array of obstacles radii, +* `~x_vector` (`std::vector`, default: `[]`) - the array of velocities of obstacles center points in x direction, +* `~y_vector` (`std::vector`, default: `[]`) - the array of velocities of obstacles center points in y direction. + +The package comes with Rviz panel for this node. + +----------------------- +

+ Rviz panel for the obstacle_publisher node. +
+ Fig. 8. Rviz panel for the `obstacle_publisher` node. +

+ +----------------------- + +## 2. The messages + +The package provides three custom message types. All of their numerical values are provided in SI units. + +* `CircleObstacle` + - `geometry_msgs/Point center` - center of circular obstacle, + - `geometry_msgs/Vector3 velocity` - linear velocity of circular obstacle, + - `float64 radius` - radius of circular obstacle with added safety margin, + - `float64 true_radius` - measured radius of obstacle without the safety margin. +* `SegmentObstacle` + - `geometry_msgs/Point first_point` - first point of the segment (in counter-clockwise direction), + - `geometry_msgs/Point last_point` - end point of the segment. +* `Obstacles` + - `Header header` + - `obstacle_detector/SegmentObstacle[] segments` + - `obstacle_detector/CircleObstacle[] circles` + +## 3. The launch files + +Provided launch files are good examples of how to use `obstacle_detector` package. They give a full list of parameters used by each of provided nodes. +* `demo.launch` - Plays a rosbag with recorded scans and starts all of the nodes with Rviz configured with appropriate panels. +* `nodes_example.launch` - Runs all of the nodes with their parameters set to default values. +* `nodelets_example.launch` - Runs all of the nodelets with their parameters set to default values. + +## 4. The displays + +For better visual effects, appropriate Rviz display for `Obstacles` messages was prepared. Via its properties, one can change the colors of the obstacles. + +## 5. Other interesting forks of this repository + +- Lots of improvements: + + https://github.com/6RiverSystems/obstacle_detector/tree/6RS + + Melodic: + + https://github.com/6RiverSystems/obstacle_detector/tree/6RS-melodic + https://github.com/6RiverSystems/obstacle_detector/tree/feature/lidar-tracking + +- Rectangular obstacles: + + https://github.com/tysik/obstacle_detector/compare/master...icsl-Jeon:obstacle_detector:master + +- Dynamic map subtraction, robot base velocity compensation: + + https://github.com/tysik/obstacle_detector/compare/master...bangyii:obstacle_detector:master + +- Fix start/end of laser bug: + + https://github.com/tysik/obstacle_detector/compare/master...seiga-k:obstacle_detector:master + +- Obstacles to PoseArray conversion for better Visualization: + + https://github.com/tysik/obstacle_detector/compare/master...gaoyangu:obstacle_detector:rikirobot + +- More obstacle information (can it move?) publishing: + + https://github.com/tysik/obstacle_detector/compare/master...Xvdgeest:obstacle_detector:master + +- Publishing of Polygons in addition to Segments: + + https://github.com/tysik/obstacle_detector/compare/master...FelixTFD:obstacle_detector:master + +- Different circle merging logic: + + https://github.com/tysik/obstacle_detector/compare/master...ljburtz:obstacle_detector:circle_merge_logic + +- Cone detection: + + https://github.com/tysik/obstacle_detector/compare/master...Magnusgaertner:obstacle_detector:master + +Author: +_Mateusz Przybyla_ + diff --git a/src/obstacle_detector/icons/classes/Obstacle Extractor.png b/src/obstacle_detector/icons/classes/Obstacle Extractor.png new file mode 100644 index 0000000..bf97282 Binary files /dev/null and b/src/obstacle_detector/icons/classes/Obstacle Extractor.png differ diff --git a/src/obstacle_detector/icons/classes/Obstacle Publisher.png b/src/obstacle_detector/icons/classes/Obstacle Publisher.png new file mode 100644 index 0000000..61a4902 Binary files /dev/null and b/src/obstacle_detector/icons/classes/Obstacle Publisher.png differ diff --git a/src/obstacle_detector/icons/classes/Obstacle Tracker 2.png b/src/obstacle_detector/icons/classes/Obstacle Tracker 2.png new file mode 100644 index 0000000..cf74075 Binary files /dev/null and b/src/obstacle_detector/icons/classes/Obstacle Tracker 2.png differ diff --git a/src/obstacle_detector/icons/classes/Obstacle Tracker.png b/src/obstacle_detector/icons/classes/Obstacle Tracker.png new file mode 100644 index 0000000..4c1c767 Binary files /dev/null and b/src/obstacle_detector/icons/classes/Obstacle Tracker.png differ diff --git a/src/obstacle_detector/icons/classes/Obstacles.png b/src/obstacle_detector/icons/classes/Obstacles.png new file mode 100644 index 0000000..ed91d08 Binary files /dev/null and b/src/obstacle_detector/icons/classes/Obstacles.png differ diff --git a/src/obstacle_detector/icons/classes/Scans Merger.png b/src/obstacle_detector/icons/classes/Scans Merger.png new file mode 100644 index 0000000..b1dff2c Binary files /dev/null and b/src/obstacle_detector/icons/classes/Scans Merger.png differ diff --git a/src/obstacle_detector/include/obstacle_detector/displays/circle_visual.h b/src/obstacle_detector/include/obstacle_detector/displays/circle_visual.h new file mode 100644 index 0000000..22011c7 --- /dev/null +++ b/src/obstacle_detector/include/obstacle_detector/displays/circle_visual.h @@ -0,0 +1,80 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2017, Poznan University of Technology + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Poznan University of Technology nor the names + * of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Author: Mateusz Przybyla + */ + +#pragma once + +#ifndef Q_MOC_RUN +#include + +#include +#include +#include + +#include +#include +#include +#endif + +namespace obstacles_display +{ + +class CircleVisual +{ +public: + CircleVisual(Ogre::SceneManager* scene_manager, Ogre::SceneNode* parent_node); + + virtual ~CircleVisual(); + + void setData(const obstacle_detector::CircleObstacle& circle); + void setFramePose(const Ogre::Vector3& position, const Ogre::Quaternion& orientation); + void setMainColor(float r, float g, float b, float a); + void setMarginColor(float r, float g, float b, float a); + +private: + boost::shared_ptr obstacle_; + boost::shared_ptr margin_; + boost::shared_ptr velocity_; + rviz::MovableText* text_; + + Ogre::Vector3 center_position_; + + Ogre::SceneNode* frame_node_obstacle_; + Ogre::SceneNode* frame_node_margin_; + Ogre::SceneNode* frame_node_velocity_; + Ogre::SceneNode* frame_node_text_; + Ogre::SceneManager* scene_manager_; +}; + +} // end namespace obstacles_display diff --git a/src/obstacle_detector/include/obstacle_detector/displays/obstacles_display.h b/src/obstacle_detector/include/obstacle_detector/displays/obstacles_display.h new file mode 100644 index 0000000..7538234 --- /dev/null +++ b/src/obstacle_detector/include/obstacle_detector/displays/obstacles_display.h @@ -0,0 +1,88 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2017, Poznan University of Technology + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Poznan University of Technology nor the names + * of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Author: Mateusz Przybyla + */ + +#pragma once + +#ifndef Q_MOC_RUN +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include "obstacle_detector/displays/circle_visual.h" +#include "obstacle_detector/displays/segment_visual.h" +#endif + +namespace obstacles_display +{ + +class ObstaclesDisplay: public rviz::MessageFilterDisplay +{ +Q_OBJECT +public: + ObstaclesDisplay(); + virtual ~ObstaclesDisplay(); + +protected: + virtual void onInitialize(); + virtual void reset(); + +private Q_SLOTS: + void updateCircleColor(); + void updateSegmentColor(); + void updateAlpha(); + void updateThickness(); + +private: + void processMessage(const obstacle_detector::Obstacles::ConstPtr& obstacles_msg); + + std::vector< boost::shared_ptr > circle_visuals_; + std::vector< boost::shared_ptr > segment_visuals_; + + rviz::ColorProperty* circle_color_property_; + rviz::ColorProperty* margin_color_property_; + rviz::ColorProperty* segment_color_property_; + rviz::FloatProperty* alpha_property_; + rviz::FloatProperty* thickness_property_; +}; + +} // end namespace obstacles_display diff --git a/src/obstacle_detector/include/obstacle_detector/displays/segment_visual.h b/src/obstacle_detector/include/obstacle_detector/displays/segment_visual.h new file mode 100644 index 0000000..3807b6f --- /dev/null +++ b/src/obstacle_detector/include/obstacle_detector/displays/segment_visual.h @@ -0,0 +1,79 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2017, Poznan University of Technology + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Poznan University of Technology nor the names + * of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Author: Mateusz Przybyla + */ + +#pragma once + +#ifndef Q_MOC_RUN +#include + +#include +#include +#include + +#include +#include +#include +#include +#endif + +namespace obstacles_display +{ + +class SegmentVisual +{ +public: + SegmentVisual(Ogre::SceneManager* scene_manager, Ogre::SceneNode* parent_node); + + virtual ~SegmentVisual(); + + void setData(const obstacle_detector::SegmentObstacle& segment); + void setFramePose(const Ogre::Vector3& position, const Ogre::Quaternion& orientation); + void setColor(float r, float g, float b, float a); + void setWidth(float w); + +private: + boost::shared_ptr line_; + boost::shared_ptr velocity_; + rviz::MovableText* text_; + + Ogre::Vector3 center_position_; + + Ogre::SceneNode* frame_node_line_; + Ogre::SceneNode* frame_node_velocity_; + Ogre::SceneNode* frame_node_text_; + Ogre::SceneManager* scene_manager_; +}; + +} // end namespace obstacles_display diff --git a/src/obstacle_detector/include/obstacle_detector/obstacle_extractor.h b/src/obstacle_detector/include/obstacle_detector/obstacle_extractor.h new file mode 100644 index 0000000..0e10ff7 --- /dev/null +++ b/src/obstacle_detector/include/obstacle_detector/obstacle_extractor.h @@ -0,0 +1,124 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2017, Poznan University of Technology + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Poznan University of Technology nor the names + * of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Author: Mateusz Przybyla + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "obstacle_detector/utilities/point.h" +#include "obstacle_detector/utilities/segment.h" +#include "obstacle_detector/utilities/circle.h" +#include "obstacle_detector/utilities/point_set.h" + +namespace obstacle_detector +{ + +class ObstacleExtractor +{ +public: + ObstacleExtractor(ros::NodeHandle& nh, ros::NodeHandle& nh_local); + ~ObstacleExtractor(); + +private: + bool updateParams(std_srvs::Empty::Request& req, std_srvs::Empty::Response& res); + void scanCallback(const sensor_msgs::LaserScan::ConstPtr scan_msg); + void pclCallback(const sensor_msgs::PointCloud::ConstPtr pcl_msg); + + void initialize() { std_srvs::Empty empt; updateParams(empt.request, empt.response); } + + void processPoints(); + void groupPoints(); + void publishObstacles(); + + void detectSegments(const PointSet& point_set); + void mergeSegments(); + bool compareSegments(const Segment& s1, const Segment& s2, Segment& merged_segment); + bool checkSegmentsProximity(const Segment& s1, const Segment& s2); + bool checkSegmentsCollinearity(const Segment& segment, const Segment& s1, const Segment& s2); + + void detectCircles(); + void mergeCircles(); + bool compareCircles(const Circle& c1, const Circle& c2, Circle& merged_circle); + + ros::NodeHandle nh_; + ros::NodeHandle nh_local_; + + ros::Subscriber scan_sub_; + ros::Subscriber pcl_sub_; + ros::Publisher obstacles_pub_; + ros::ServiceServer params_srv_; + + ros::Time stamp_; + std::string base_frame_id_; + tf::TransformListener tf_listener_; + + std::list input_points_; + std::list segments_; + std::list circles_; + + // Parameters + bool p_active_; + bool p_use_scan_; + bool p_use_pcl_; + + bool p_use_split_and_merge_; + bool p_circles_from_visibles_; + bool p_discard_converted_segments_; + bool p_transform_coordinates_; + + int p_min_group_points_; + + double p_distance_proportion_; + double p_max_group_distance_; + double p_max_split_distance_; + double p_max_merge_separation_; + double p_max_merge_spread_; + double p_max_circle_radius_; + double p_radius_enlargement_; + + double p_min_x_limit_; + double p_max_x_limit_; + double p_min_y_limit_; + double p_max_y_limit_; + + std::string p_frame_id_; +}; + +} // namespace obstacle_detector diff --git a/src/obstacle_detector/include/obstacle_detector/obstacle_publisher.h b/src/obstacle_detector/include/obstacle_detector/obstacle_publisher.h new file mode 100644 index 0000000..eb10f3b --- /dev/null +++ b/src/obstacle_detector/include/obstacle_detector/obstacle_publisher.h @@ -0,0 +1,93 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2017, Poznan University of Technology + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Poznan University of Technology nor the names + * of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Author: Mateusz Przybyla + */ + +#pragma once + +#include +#include +#include + +namespace obstacle_detector +{ + +class ObstaclePublisher +{ +public: + ObstaclePublisher(ros::NodeHandle &nh, ros::NodeHandle &nh_local); + ~ObstaclePublisher(); + +private: + bool updateParams(std_srvs::Empty::Request& req, std_srvs::Empty::Response& res); + void timerCallback(const ros::TimerEvent& e); + + void initialize() { std_srvs::Empty empt; updateParams(empt.request, empt.response); } + + void calculateObstaclesPositions(double dt); + void fusionExample(double t); + void fissionExample(double t); + void publishObstacles(); + void reset(); + + ros::NodeHandle nh_; + ros::NodeHandle nh_local_; + + ros::Publisher obstacle_pub_; + ros::ServiceServer params_srv_; + ros::Timer timer_; + + obstacle_detector::Obstacles obstacles_; + double t_; + + // Parameters + bool p_active_; + bool p_reset_; + bool p_fusion_example_; + bool p_fission_example_; + + double p_loop_rate_; + double p_sampling_time_; + double p_radius_margin_; + + std::vector p_x_vector_; + std::vector p_y_vector_; + std::vector p_r_vector_; + + std::vector p_vx_vector_; + std::vector p_vy_vector_; + + std::string p_frame_id_; +}; + +} // namespace obstacle_detector diff --git a/src/obstacle_detector/include/obstacle_detector/obstacle_tracker.h b/src/obstacle_detector/include/obstacle_detector/obstacle_tracker.h new file mode 100644 index 0000000..856bfe5 --- /dev/null +++ b/src/obstacle_detector/include/obstacle_detector/obstacle_tracker.h @@ -0,0 +1,128 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2017, Poznan University of Technology + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Poznan University of Technology nor the names + * of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Author: Mateusz Przybyla + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include "obstacle_detector/utilities/tracked_circle_obstacle.h" +#include "obstacle_detector/utilities/tracked_segment_obstacle.h" +#include "obstacle_detector/utilities/math_utilities.h" + +namespace obstacle_detector +{ + +class ObstacleTracker { +public: + ObstacleTracker(ros::NodeHandle& nh, ros::NodeHandle& nh_local); + ~ObstacleTracker(); + +private: + bool updateParams(std_srvs::Empty::Request& req, std_srvs::Empty::Response& res); + void timerCallback(const ros::TimerEvent&); + void obstaclesCallback(const obstacle_detector::Obstacles::ConstPtr new_obstacles); + void obstaclesCallbackCircles(const obstacle_detector::Obstacles::ConstPtr new_obstacles); + void obstaclesCallbackSegments(const obstacle_detector::Obstacles::ConstPtr new_obstacles); + void odomCallback(const nav_msgs::Odometry::ConstPtr &msg); + + void initialize() { std_srvs::Empty empt; updateParams(empt.request, empt.response); } + + double obstacleCostFunction(const CircleObstacle& new_obstacle, const CircleObstacle& old_obstacle); + double obstacleCostFunction(const SegmentObstacle& new_obstacle, const SegmentObstacle& old_obstacle); + void calculateCostMatrix(const std::vector& new_obstacles, arma::mat& cost_matrix); + void calculateCostMatrix(const std::vector& new_obstacles, arma::mat& cost_matrix); + void calculateRowMinIndices(const arma::mat& cost_matrix, std::vector& row_min_indices, const int T, const int U); + void calculateColMinIndices(const arma::mat& cost_matrix, std::vector& col_min_indices, const int T, const int U); + + bool fusionObstacleUsed(const int idx, const std::vector& col_min_indices, const std::vector& used_new, const std::vector& used_old); + bool fusionObstaclesCorrespond(const int idx, const int jdx, const std::vector& col_min_indices, const std::vector& used_old); + bool fissionObstacleUsed(const int idx, const int T, const std::vector& row_min_indices, const std::vector& used_new, const std::vector& used_old); + bool fissionObstaclesCorrespond(const int idx, const int jdx, const std::vector& row_min_indices, const std::vector& used_new); + + void fuseObstacles(const std::vector& fusion_indices, const std::vector& col_min_indices, + std::vector& new_tracked, const Obstacles::ConstPtr& new_obstacles); + void fissureObstacle(const std::vector& fission_indices, const std::vector& row_min_indices, + std::vector& new_tracked, const Obstacles::ConstPtr& new_obstacles); + void fuseObstacles(const std::vector& fusion_indices, const std::vector& col_min_indices, + std::vector& new_tracked, const Obstacles::ConstPtr& new_obstacles); + void fissureObstacle(const std::vector& fission_indices, const std::vector& row_min_indices, + std::vector& new_tracked, const Obstacles::ConstPtr& new_obstacles); + + void updateObstacles(); + void publishObstacles(); + + ros::NodeHandle nh_; + ros::NodeHandle nh_local_; + + ros::Subscriber obstacles_sub_; + ros::Subscriber odom_sub_; + ros::Publisher obstacles_pub_; + ros::ServiceServer params_srv_; + ros::Timer timer_; + + double radius_margin_; + obstacle_detector::Obstacles obstacles_; + nav_msgs::Odometry odom_; + + std::vector tracked_circle_obstacles_; + std::vector untracked_circle_obstacles_; + std::vector tracked_segment_obstacles_; + std::vector untracked_segment_obstacles_; + + // Parameters + bool p_active_; + bool p_copy_segments_; + bool p_compensate_robot_velocity_; + + double p_tracking_duration_; + double p_loop_rate_; + double p_sampling_time_; + double p_sensor_rate_; + double p_min_correspondence_cost_; + double p_std_correspondence_dev_; + double p_process_variance_; + double p_process_rate_variance_; + double p_measurement_variance_; + + std::string p_frame_id_; +}; + +} // namespace obstacle_detector diff --git a/src/obstacle_detector/include/obstacle_detector/panels/obstacle_extractor_panel.h b/src/obstacle_detector/include/obstacle_detector/panels/obstacle_extractor_panel.h new file mode 100644 index 0000000..dd6fccd --- /dev/null +++ b/src/obstacle_detector/include/obstacle_detector/panels/obstacle_extractor_panel.h @@ -0,0 +1,122 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2017, Poznan University of Technology + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Poznan University of Technology nor the names + * of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Author: Mateusz Przybyla + */ + +#pragma once + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace obstacle_detector +{ + +class ObstacleExtractorPanel : public rviz::Panel +{ +Q_OBJECT +public: + ObstacleExtractorPanel(QWidget* parent = 0); + + virtual void load(const rviz::Config& config); + virtual void save(rviz::Config config) const; + +private Q_SLOTS: + void processInputs(); + +private: + void verifyInputs(); + void setParams(); + void getParams(); + void evaluateParams(); + void notifyParamsUpdate(); + +private: + QCheckBox* activate_checkbox_; + QCheckBox* use_scan_checkbox_; + QCheckBox* use_pcl_checkbox_; + QCheckBox* use_split_merge_checkbox_; + QCheckBox* circ_from_visib_checkbox_; + QCheckBox* discard_segments_checkbox_; + QCheckBox* transform_coords_checkbox_; + + QLineEdit* min_n_input_; + QLineEdit* dist_prop_input_; + QLineEdit* group_dist_input_; + QLineEdit* split_dist_input_; + QLineEdit* merge_sep_input_; + QLineEdit* merge_spread_input_; + QLineEdit* max_radius_input_; + QLineEdit* radius_enl_input_; + QLineEdit* frame_id_input_; + + ros::NodeHandle nh_; + ros::NodeHandle nh_local_; + + ros::ServiceClient params_cli_; + + // Parameters + bool p_active_; + bool p_use_scan_; + bool p_use_pcl_; + + bool p_use_split_and_merge_; + bool p_circles_from_visibles_; + bool p_discard_converted_segments_; + bool p_transform_coordinates_; + + int p_min_group_points_; + + double p_distance_proportion_; + double p_max_group_distance_; + + double p_max_split_distance_; + double p_max_merge_separation_; + double p_max_merge_spread_; + double p_max_circle_radius_; + double p_radius_enlargement_; + + std::string p_frame_id_; +}; + +} // namespace obstacle_detector diff --git a/src/obstacle_detector/include/obstacle_detector/panels/obstacle_publisher_panel.h b/src/obstacle_detector/include/obstacle_detector/panels/obstacle_publisher_panel.h new file mode 100644 index 0000000..ba331c2 --- /dev/null +++ b/src/obstacle_detector/include/obstacle_detector/panels/obstacle_publisher_panel.h @@ -0,0 +1,125 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2017, Poznan University of Technology + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Poznan University of Technology nor the names + * of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Author: Mateusz Przybyla + */ + +#pragma once + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace obstacle_detector +{ + +class ObstaclePublisherPanel : public rviz::Panel +{ +Q_OBJECT +public: + ObstaclePublisherPanel(QWidget* parent = 0); + + virtual void load(const rviz::Config& config); + virtual void save(rviz::Config config) const; + +private Q_SLOTS: + void processInputs(); + void addObstacle(); + void removeObstacles(); + void reset(); + +private: + void verifyInputs(); + void setParams(); + void getParams(); + void evaluateParams(); + void notifyParamsUpdate(); + +private: + QCheckBox* activate_checkbox_; + QCheckBox* fusion_example_checkbox_; + QCheckBox* fission_example_checkbox_; + + QListWidget* obstacles_list_; + std::vector obstacles_list_items_; + + QPushButton* add_button_; + QPushButton* remove_button_; + QPushButton* reset_button_; + + QLineEdit* x_input_; + QLineEdit* y_input_; + QLineEdit* r_input_; + + QLineEdit* vx_input_; + QLineEdit* vy_input_; + + QLineEdit* frame_input_; + + ros::NodeHandle nh_; + ros::NodeHandle nh_local_; + + ros::ServiceClient params_cli_; + + double x_, y_, r_, vx_, vy_; + + // Parameters + bool p_active_; + bool p_reset_; + bool p_fusion_example_; + bool p_fission_example_; + + double p_loop_rate_; + + std::vector p_x_vector_; + std::vector p_y_vector_; + std::vector p_r_vector_; + + std::vector p_vx_vector_; + std::vector p_vy_vector_; + + std::string p_frame_id_; +}; + +} diff --git a/src/obstacle_detector/include/obstacle_detector/panels/obstacle_tracker_panel.h b/src/obstacle_detector/include/obstacle_detector/panels/obstacle_tracker_panel.h new file mode 100644 index 0000000..2ffd7de --- /dev/null +++ b/src/obstacle_detector/include/obstacle_detector/panels/obstacle_tracker_panel.h @@ -0,0 +1,102 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2017, Poznan University of Technology + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Poznan University of Technology nor the names + * of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Author: Mateusz Przybyla + */ + +#pragma once + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace obstacle_detector +{ + +class ObstacleTrackerPanel : public rviz::Panel +{ +Q_OBJECT +public: + ObstacleTrackerPanel(QWidget* parent = 0); + + virtual void load(const rviz::Config& config); + virtual void save(rviz::Config config) const; + +private Q_SLOTS: + void processInputs(); + +private: + void verifyInputs(); + void setParams(); + void getParams(); + void evaluateParams(); + void notifyParamsUpdate(); + +private: + QCheckBox* activate_checkbox_; + + QLineEdit* tracking_duration_input_; + QLineEdit* loop_rate_input_; + QLineEdit* min_corr_cost_input_; + QLineEdit* std_corr_dev_input_; + QLineEdit* process_var_input_; + QLineEdit* process_rate_var_input_; + QLineEdit* measure_var_input_; + + ros::NodeHandle nh_; + ros::NodeHandle nh_local_; + + ros::ServiceClient params_cli_; + + // Parameters + bool p_active_; + + double p_tracking_duration_; + double p_loop_rate_; + double p_min_correspondence_cost_; + double p_std_correspondence_dev_; + double p_process_variance_; + double p_process_rate_variance_; + double p_measurement_variance_; +}; + +} diff --git a/src/obstacle_detector/include/obstacle_detector/panels/scans_merger_panel.h b/src/obstacle_detector/include/obstacle_detector/panels/scans_merger_panel.h new file mode 100644 index 0000000..27ce5e5 --- /dev/null +++ b/src/obstacle_detector/include/obstacle_detector/panels/scans_merger_panel.h @@ -0,0 +1,114 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2017, Poznan University of Technology + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Poznan University of Technology nor the names + * of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Author: Mateusz Przybyla + */ + +#pragma once + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace obstacle_detector +{ + +class ScansMergerPanel : public rviz::Panel +{ +Q_OBJECT +public: + ScansMergerPanel(QWidget* parent = 0); + + virtual void load(const rviz::Config& config); + virtual void save(rviz::Config config) const; + +private Q_SLOTS: + void processInputs(); + +private: + void verifyInputs(); + void setParams(); + void getParams(); + void evaluateParams(); + void notifyParamsUpdate(); + +private: + QCheckBox* activate_checkbox_; + QCheckBox* scan_checkbox_; + QCheckBox* pcl_checkbox_; + + QLineEdit* n_input_; + QLineEdit* r_min_input_; + QLineEdit* r_max_input_; + + QLineEdit* x_min_input_; + QLineEdit* x_max_input_; + QLineEdit* y_min_input_; + QLineEdit* y_max_input_; + + QLineEdit* fixed_frame_id_input_; + QLineEdit* target_frame_id_input_; + + ros::NodeHandle nh_; + ros::NodeHandle nh_local_; + + ros::ServiceClient params_cli_; + + // Parameters + bool p_active_; + bool p_publish_scan_; + bool p_publish_pcl_; + + int p_ranges_num_; + + double p_min_scanner_range_; + double p_max_scanner_range_; + + double p_min_x_range_; + double p_max_x_range_; + double p_min_y_range_; + double p_max_y_range_; + + std::string p_fixed_frame_id_; + std::string p_target_frame_id_; +}; + +} // namespace obstacle_detector diff --git a/src/obstacle_detector/include/obstacle_detector/scans_merger.h b/src/obstacle_detector/include/obstacle_detector/scans_merger.h new file mode 100644 index 0000000..0bbcd58 --- /dev/null +++ b/src/obstacle_detector/include/obstacle_detector/scans_merger.h @@ -0,0 +1,103 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2017, Poznan University of Technology + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Poznan University of Technology nor the names + * of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Author: Mateusz Przybyla + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace obstacle_detector +{ + +class ScansMerger +{ +public: + ScansMerger(ros::NodeHandle& nh, ros::NodeHandle& nh_local); + ~ScansMerger(); + +private: + bool updateParams(std_srvs::Empty::Request& req, std_srvs::Empty::Response& res); + void frontScanCallback(const sensor_msgs::LaserScan::ConstPtr front_scan); + void rearScanCallback(const sensor_msgs::LaserScan::ConstPtr rear_scan); + + void initialize() { std_srvs::Empty empt; updateParams(empt.request, empt.response); } + + void publishMessages(); + + ros::NodeHandle nh_; + ros::NodeHandle nh_local_; + + ros::ServiceServer params_srv_; + + ros::Subscriber front_scan_sub_; + ros::Subscriber rear_scan_sub_; + ros::Publisher scan_pub_; + ros::Publisher pcl_pub_; + + tf::TransformListener tf_ls_; + laser_geometry::LaserProjection projector_; + + bool front_scan_received_; + bool rear_scan_received_; + bool front_scan_error_; + bool rear_scan_error_; + + sensor_msgs::PointCloud front_pcl_; + sensor_msgs::PointCloud rear_pcl_; + + // Parameters + bool p_active_; + bool p_publish_scan_; + bool p_publish_pcl_; + + int p_ranges_num_; + + double p_min_scanner_range_; + double p_max_scanner_range_; + double p_min_x_range_; + double p_max_x_range_; + double p_min_y_range_; + double p_max_y_range_; + + std::string p_fixed_frame_id_; + std::string p_target_frame_id_; +}; + +} // namespace obstacle_detector diff --git a/src/obstacle_detector/include/obstacle_detector/utilities/circle.h b/src/obstacle_detector/include/obstacle_detector/utilities/circle.h new file mode 100644 index 0000000..614284c --- /dev/null +++ b/src/obstacle_detector/include/obstacle_detector/utilities/circle.h @@ -0,0 +1,69 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2017, Poznan University of Technology + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Poznan University of Technology nor the names + * of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Author: Mateusz Przybyla + */ + +#pragma once + +#include "obstacle_detector/utilities/point.h" +#include "obstacle_detector/utilities/segment.h" + +namespace obstacle_detector +{ + +class Circle +{ +public: + Circle(const Point& p = Point(), const double r = 0.0) : center(p), radius(r) { } + + /* + * Create a circle by taking the segment as a base of equilateral + * triangle. The circle is circumscribed on this triangle. + */ + Circle(const Segment& s) { + radius = 0.5773502 * s.length(); // sqrt(3)/3 * length + center = (s.first_point + s.last_point - radius * s.normal()) / 2.0; + point_sets = s.point_sets; + } + + double distanceTo(const Point& p) { return (p - center).length() - radius; } + + friend std::ostream& operator<<(std::ostream& out, const Circle& c) + { out << "C: " << c.center << ", R: " << c.radius; return out; } + + Point center; + double radius; + std::vector point_sets; +}; + +} // namespace obstacle_detector diff --git a/src/obstacle_detector/include/obstacle_detector/utilities/figure_fitting.h b/src/obstacle_detector/include/obstacle_detector/utilities/figure_fitting.h new file mode 100644 index 0000000..0161742 --- /dev/null +++ b/src/obstacle_detector/include/obstacle_detector/utilities/figure_fitting.h @@ -0,0 +1,203 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2017, Poznan University of Technology + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Poznan University of Technology nor the names + * of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Author: Mateusz Przybyla + */ + +#pragma once + +#include + +#include "obstacle_detector/utilities/point.h" +#include "obstacle_detector/utilities/segment.h" +#include "obstacle_detector/utilities/circle.h" + +namespace obstacle_detector +{ + +/* + * Returns a total best fit approximation of + * segment based on given point set. The equation + * used for fitting is given by + * Ax + By = -C + * and the A, B, C parameters are normalized. + */ +Segment fitSegment(const PointSet& point_set) { + static int num_calls = 0; + num_calls++; + + int N = point_set.num_points; + assert(N >= 2); + + arma::mat input = arma::mat(N, 2).zeros(); // [x_i, y_i] + arma::vec output = arma::vec(N).ones(); // [-C] + arma::vec params = arma::vec(2).zeros(); // [A ; B] + + PointIterator point = point_set.begin; + for (int i = 0; i < N; ++i) { + input(i, 0) = point->x; + input(i, 1) = point->y; + std::advance(point, 1); + } + + // Find A and B coefficients from linear regression (assuming C = -1.0) + params = arma::pinv(input) * output; + + double A, B, C; + A = params(0); + B = params(1); + C = -1.0; + + // Find end points + Point p1 = *point_set.begin; + Point p2 = *point_set.end; + + Segment segment(p1, p2); + segment.point_sets.push_back(point_set); + + double D = (A * A + B * B); + + // Project end points on the line + if (D > 0.0) { + Point projected_p1, projected_p2; + + projected_p1.x = ( B * B * p1.x - A * B * p1.y - A * C) / D; + projected_p1.y = (-A * B * p1.x + A * A * p1.y - B * C) / D; + + projected_p2.x = ( B * B * p2.x - A * B * p2.y - A * C) / D; + projected_p2.y = (-A * B * p2.x + A * A * p2.y - B * C) / D; + + segment.first_point = projected_p1; + segment.last_point = projected_p2; + } + + return segment; +} + +Segment fitSegment(const std::vector& point_sets) { + static int num_calls = 0; + num_calls++; + + int N = 0; + for (PointSet ps : point_sets) + N += ps.num_points; + + assert(N >= 2); + + arma::mat input = arma::mat(N, 2).zeros(); // [x_i, y_i] + arma::vec output = arma::vec(N).ones(); // [-C] + arma::vec params = arma::vec(2).zeros(); // [A ; B] + + int n = 0; + for (PointSet ps : point_sets) { + PointIterator point = ps.begin; + for (int i = 0; i < ps.num_points; ++i) { + input(i + n, 0) = point->x; + input(i + n, 1) = point->y; + std::advance(point, 1); + } + + n += ps.num_points; + } + + // Find A and B coefficients from linear regression (assuming C = -1.0) + params = arma::pinv(input) * output; + + double A, B, C; + A = params(0); + B = params(1); + C = -1.0; + + // Find end points + Point p1 = *point_sets.front().begin; + Point p2 = *point_sets.back().end; + + Segment segment(p1, p2); + segment.point_sets = point_sets; + + double D = (A * A + B * B); + + // Project end points on the line + if (D > 0.0) { + Point projected_p1, projected_p2; + + projected_p1.x = ( B * B * p1.x - A * B * p1.y - A * C) / D; + projected_p1.y = (-A * B * p1.x + A * A * p1.y - B * C) / D; + + projected_p2.x = ( B * B * p2.x - A * B * p2.y - A * C) / D; + projected_p2.y = (-A * B * p2.x + A * A * p2.y - B * C) / D; + + segment.first_point = projected_p1; + segment.last_point = projected_p2; + } + + return segment; +} + +/* + * Returns a total best fit approximation of + * a circle based on given point set. The equation + * used for fitting is given by + * a1 * x + a2 * y + a3 = -(x^2 + y^2) + * where parameters a1, a2, a3 are obtained from + * circle equation + * (x-x0)^2 + (y-y0)^2 = r^2. + */ +Circle fitCircle(const std::list& point_set) +{ + int N = point_set.size(); + assert(N >= 3); + + arma::mat input = arma::mat(N, 3).zeros(); // [x_i, y_i, 1] + arma::vec output = arma::vec(N).zeros(); // [-(x_i^2 + y_i^2)] + arma::vec params = arma::vec(3).zeros(); // [a_1 ; a_2 ; a_3] + + int i = 0; + for (const Point& point : point_set) { + input(i, 0) = point.x; + input(i, 1) = point.y; + input(i, 2) = 1.0; + + output(i) = -(pow(point.x, 2) + pow(point.y, 2)); + i++; + } + + // Find a_1, a_2 and a_3 coefficients from linear regression + params = arma::pinv(input) * output; + + Point center(-params(0) / 2.0, -params(1) / 2.0); + double radius = sqrt((params(0) * params(0) + params(1) * params(1)) / 4.0 - params(2)); + + return Circle(center, radius); +} + +} // namespace obstacle_detector diff --git a/src/obstacle_detector/include/obstacle_detector/utilities/kalman.h b/src/obstacle_detector/include/obstacle_detector/utilities/kalman.h new file mode 100644 index 0000000..879af16 --- /dev/null +++ b/src/obstacle_detector/include/obstacle_detector/utilities/kalman.h @@ -0,0 +1,108 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2017, Poznan University of Technology + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Poznan University of Technology nor the names + * of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Author: Mateusz Przybyla + */ + +#pragma once + +#include + +class KalmanFilter +{ +public: + KalmanFilter(uint dim_in, uint dim_out, uint dim_state) : l(dim_in), m(dim_out), n(dim_state) { + using arma::mat; + using arma::vec; + + A = mat(n,n).eye(); + B = mat(n,l).zeros(); + C = mat(m,n).zeros(); + + Q = mat(n,n).eye(); + R = mat(m,m).eye(); + P = mat(n,n).eye(); + + K = mat(n,m).eye(); + I = arma::eye(n,n); + + u = vec(l).zeros(); + q_pred = vec(n).zeros(); + q_est = vec(n).zeros(); + y = vec(m).zeros(); + } + + void predictState() { + q_pred = A * q_est + B * u; + P = A * P * trans(A) + Q; + } + + void correctState() { + K = P * trans(C) * inv(C * P * trans(C) + R); + q_est = q_pred + K * (y - C * q_pred); + P = (I - K * C) * P; + } + + void updateState() { + predictState(); + correctState(); + } + +public: + // System matrices: + arma::mat A; // State + arma::mat B; // Input + arma::mat C; // Output + + // Covariance matrices: + arma::mat Q; // Process + arma::mat R; // Measurement + arma::mat P; // Estimate error + + // Kalman gain matrix: + arma::mat K; + + // Identity matrix + arma::mat I; + + // Signals: + arma::vec u; // Input + arma::vec q_pred; // Predicted state + arma::vec q_est; // Estimated state + arma::vec y; // Measurement + +private: + // Dimensions: + uint l; // Input + uint m; // Output + uint n; // State +}; diff --git a/src/obstacle_detector/include/obstacle_detector/utilities/math_utilities.h b/src/obstacle_detector/include/obstacle_detector/utilities/math_utilities.h new file mode 100644 index 0000000..54d4819 --- /dev/null +++ b/src/obstacle_detector/include/obstacle_detector/utilities/math_utilities.h @@ -0,0 +1,108 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2017, Poznan University of Technology + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Poznan University of Technology nor the names + * of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Author: Mateusz Przybyla + */ + +#pragma once + +#include +#include +#include + +#include "obstacle_detector/utilities/point.h" + +namespace obstacle_detector +{ + +inline double signum(double x) { return (x < 0.0) ? -1.0 : 1.0; } +inline double abs(double x) { return (x < 0.0) ? -x : x; } +inline double max(double x, double y) { return (x > y) ? x : y; } + +inline double length(const geometry_msgs::Point& point) { + return sqrt(point.x * point.x + point.y * point.y); +} + +inline double squaredLength(const geometry_msgs::Point& point) { + return point.x * point.x + point.y * point.y; +} + +inline double length(const geometry_msgs::Vector3& vec) { + return sqrt(vec.x * vec.x + vec.y * vec.y); +} + +inline double squaredLength(const geometry_msgs::Vector3& vec) { + return vec.x * vec.x + vec.y * vec.y; +} + +inline geometry_msgs::Point transformPoint(const geometry_msgs::Point& point, double x, double y, double theta) { + geometry_msgs::Point p; + + p.x = point.x * cos(theta) - point.y * sin(theta) + x; + p.y = point.x * sin(theta) + point.y * cos(theta) + y; + + return p; +} + +inline geometry_msgs::Point32 transformPoint(const geometry_msgs::Point32& point, double x, double y, double theta) { + geometry_msgs::Point32 p; + + p.x = point.x * cos(theta) - point.y * sin(theta) + x; + p.y = point.x * sin(theta) + point.y * cos(theta) + y; + + return p; +} + +inline Point transformPoint(const Point point, double x, double y, double theta) { + Point p; + + p.x = point.x * cos(theta) - point.y * sin(theta) + x; + p.y = point.x * sin(theta) + point.y * cos(theta) + y; + + return p; +} + +inline Point transformPoint(const Point& point, const tf::StampedTransform& transform) { + tf::Vector3 v(point.x, point.y, 0); + v = transform * v; + + return {v.x(), v.y()}; +} + +inline bool checkPointInLimits(const geometry_msgs::Point32& p, double x_min, double x_max, double y_min, double y_max) { + if ((p.x > x_max) || (p.x < x_min) || (p.y > y_max) || (p.y < y_min)) + return false; + else + return true; +} + +} // namespace obstacle_detector diff --git a/src/obstacle_detector/include/obstacle_detector/utilities/point.h b/src/obstacle_detector/include/obstacle_detector/utilities/point.h new file mode 100644 index 0000000..7874b63 --- /dev/null +++ b/src/obstacle_detector/include/obstacle_detector/utilities/point.h @@ -0,0 +1,92 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2017, Poznan University of Technology + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Poznan University of Technology nor the names + * of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Author: Mateusz Przybyla + */ + +#pragma once + +#include +#include + +namespace obstacle_detector +{ + +class Point +{ +public: + Point(double x = 0.0, double y = 0.0, double range = 0.0) : x(x), y(y), range(range) {} + Point(const Point& p) : x(p.x), y(p.y), range(p.range) {} + static Point fromPoolarCoords(const double r, const double phi) { return Point(r * cos(phi), r * sin(phi)); } + + double getRange() const { return range > 0.0 ? range : length(); } + double length() const { return sqrt(pow(x, 2.0) + pow(y, 2.0)); } + double lengthSquared() const { return pow(x, 2.0) + pow(y, 2.0); } + double angle() const { return atan2(y, x); } + double angleDeg() const { return 180.0 * atan2(y, x) / M_PI; } + double dot(const Point& p) const { return x * p.x + y * p.y; } + double cross(const Point& p) const { return x * p.y - y * p.x; } + + Point normalized() { return (length() > 0.0) ? *this / length() : *this; } + Point reflected(const Point& normal) const { return *this - 2.0 * normal * (normal.dot(*this)); } + Point perpendicular() const { return Point(-y, x); } + + friend Point operator+ (const Point& p1, const Point& p2) { return Point(p1.x + p2.x, p1.y + p2.y); } + friend Point operator- (const Point& p1, const Point& p2) { return Point(p1.x - p2.x, p1.y - p2.y); } + friend Point operator* (const double f, const Point& p) { return Point(f * p.x, f * p.y); } + friend Point operator* (const Point& p, const double f) { return Point(f * p.x, f * p.y); } + friend Point operator/ (const Point& p, const double f) { return (f != 0.0) ? Point(p.x / f, p.y / f) : Point(); } + + Point operator- () { return Point(-x, -y); } + Point operator+ () { return Point( x, y); } + + Point& operator= (const Point& p) { if (this != &p) { x = p.x; y = p.y; } return *this; } + Point& operator+= (const Point& p) { x += p.x; y += p.y; return *this; } + Point& operator-= (const Point& p) { x -= p.x; y -= p.y; return *this; } + + friend bool operator== (const Point& p1, const Point& p2) { return (p1.x == p2.x && p1.y == p2.y); } + friend bool operator!= (const Point& p1, const Point& p2) { return !(p1 == p2); } + friend bool operator< (const Point& p1, const Point& p2) { return (p1.lengthSquared() < p2.lengthSquared()); } + friend bool operator<= (const Point& p1, const Point& p2) { return (p1.lengthSquared() <= p2.lengthSquared()); } + friend bool operator> (const Point& p1, const Point& p2) { return (p1.lengthSquared() > p2.lengthSquared()); } + friend bool operator>= (const Point& p1, const Point& p2) { return (p1.lengthSquared() >= p2.lengthSquared()); } + friend bool operator! (const Point& p1) { return (p1.x == 0.0 && p1.y == 0.0); } + + friend std::ostream& operator<<(std::ostream& out, const Point& p) + { out << "(" << p.x << ", " << p.y << ") with range " << p.range << " to origin"; return out; } + + double x; + double y; + double range = 0.0; // Distance w.r.t. lidar scan origin +}; + +} // namespace obstacle_detector diff --git a/src/obstacle_detector/include/obstacle_detector/utilities/point_set.h b/src/obstacle_detector/include/obstacle_detector/utilities/point_set.h new file mode 100644 index 0000000..b0ad481 --- /dev/null +++ b/src/obstacle_detector/include/obstacle_detector/utilities/point_set.h @@ -0,0 +1,58 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2017, Poznan University of Technology + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Poznan University of Technology nor the names + * of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Author: Mateusz Przybyla + */ + +#pragma once + +#include + +#include "obstacle_detector/utilities/point.h" + +namespace obstacle_detector +{ + +typedef std::list::iterator PointIterator; + +class PointSet +{ +public: + PointSet() : num_points(0), is_visible(false) {} + + PointIterator begin, end; // The iterators point to the list of points existing somewhere else + int num_points; + bool is_visible; // The point set is not occluded by any other point set +}; + +} // namespace obstacle_detector + diff --git a/src/obstacle_detector/include/obstacle_detector/utilities/segment.h b/src/obstacle_detector/include/obstacle_detector/utilities/segment.h new file mode 100644 index 0000000..26ba730 --- /dev/null +++ b/src/obstacle_detector/include/obstacle_detector/utilities/segment.h @@ -0,0 +1,121 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2017, Poznan University of Technology + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Poznan University of Technology nor the names + * of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Author: Mateusz Przybyla + */ + +#pragma once + +#include + +#include "obstacle_detector/utilities/point.h" +#include "obstacle_detector/utilities/point_set.h" + +namespace obstacle_detector +{ + +class Segment +{ +public: + Segment(const Point& p1 = Point(), const Point& p2 = Point()) { + // Swap if not counter-clockwise + if (p1.cross(p2) > 0.0) + first_point = p1, last_point = p2; + else + first_point = p2, last_point = p1; + } + + double length() const { + return (last_point - first_point).length(); + } + + double lengthSquared() const { + return (last_point - first_point).lengthSquared(); + } + + Point normal() const { + return (last_point - first_point).perpendicular().normalized(); + } + + Point projection(const Point& p) const { + Point a = last_point - first_point; + Point b = p - first_point; + return first_point + a.dot(b) * a / a.lengthSquared(); + } + + Point trueProjection(const Point& p) const { + Point a = last_point - first_point; + Point b = p - first_point; + Point c = p - last_point; + + double t = a.dot(b) / a.lengthSquared(); + + if (t < 0.0) + return (first_point); + else if (t > 1.0) + return (last_point); + else + return first_point + a.dot(b) * a / a.lengthSquared(); + } + + double distanceTo(const Point& p) const { + return (p - projection(p)).length(); + } + + double trueDistanceTo(const Point& p) const { + Point a = last_point - first_point; + Point b = p - first_point; + Point c = p - last_point; + + double t = a.dot(b) / a.lengthSquared(); + + if (t < 0.0) + return b.length(); + else if (t > 1.0) + return c.length(); + + Point projection = first_point + t * a; + return (p - projection).length(); + } + + + friend std::ostream& operator<<(std::ostream& out, const Segment& s) { + out << "p1: " << s.first_point << ", p2: " << s.last_point; + return out; + } + + Point first_point; + Point last_point; + std::vector point_sets; +}; + +} // namespace obstacle_detector diff --git a/src/obstacle_detector/include/obstacle_detector/utilities/tracked_circle_obstacle.h b/src/obstacle_detector/include/obstacle_detector/utilities/tracked_circle_obstacle.h new file mode 100644 index 0000000..4431cf3 --- /dev/null +++ b/src/obstacle_detector/include/obstacle_detector/utilities/tracked_circle_obstacle.h @@ -0,0 +1,183 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2017, Poznan University of Technology + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Poznan University of Technology nor the names + * of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Author: Mateusz Przybyla + */ + +#pragma once + +#include +#include "obstacle_detector/utilities/tracked_obstacle.h" +#include "obstacle_detector/utilities/kalman.h" + +namespace obstacle_detector +{ + +class TrackedCircleObstacle { +public: + TrackedCircleObstacle(const CircleObstacle& obstacle) : obstacle_(obstacle), kf_x_(0, 1, 2), kf_y_(0, 1, 2), kf_r_(0, 1, 2) { + fade_counter_ = s_fade_counter_size_; + setNewUid(); + initKF(); + } + + void predictState() { + kf_x_.predictState(); + kf_y_.predictState(); + kf_r_.predictState(); + + obstacle_.center.x = kf_x_.q_pred(0); + obstacle_.center.y = kf_y_.q_pred(0); + + obstacle_.velocity.x = kf_x_.q_pred(1); + obstacle_.velocity.y = kf_y_.q_pred(1); + + obstacle_.radius = kf_r_.q_pred(0); + + fade_counter_--; + } + + void correctState(const CircleObstacle& new_obstacle) { + kf_x_.y(0) = new_obstacle.center.x; + kf_y_.y(0) = new_obstacle.center.y; + kf_r_.y(0) = new_obstacle.radius; + + kf_x_.correctState(); + kf_y_.correctState(); + kf_r_.correctState(); + + obstacle_.center.x = kf_x_.q_est(0); + obstacle_.center.y = kf_y_.q_est(0); + + obstacle_.velocity.x = kf_x_.q_est(1); + obstacle_.velocity.y = kf_y_.q_est(1); + + obstacle_.radius = kf_r_.q_est(0); + + fade_counter_ = s_fade_counter_size_; + } + + void updateState() { + kf_x_.predictState(); + kf_y_.predictState(); + kf_r_.predictState(); + + kf_x_.correctState(); + kf_y_.correctState(); + kf_r_.correctState(); + + obstacle_.center.x = kf_x_.q_est(0); + obstacle_.center.y = kf_y_.q_est(0); + + obstacle_.velocity.x = kf_x_.q_est(1); + obstacle_.velocity.y = kf_y_.q_est(1); + + obstacle_.radius = kf_r_.q_est(0); + + fade_counter_--; + } + + static void setSamplingTime(double tp) { + s_sampling_time_ = tp; + } + + static void setCounterSize(int size) { + s_fade_counter_size_ = size; + } + + static void setCovariances(double process_var, double process_rate_var, double measurement_var) { + s_process_variance_ = process_var; + s_process_rate_variance_ = process_rate_var; + s_measurement_variance_ = measurement_var; + } + + void setNewUid() { obstacle_.uid = uid_next_++; } + bool hasFaded() const { return ((fade_counter_ <= 0) ? true : false); } + const CircleObstacle& getObstacle() const { return obstacle_; } + const KalmanFilter& getKFx() const { return kf_x_; } + const KalmanFilter& getKFy() const { return kf_y_; } + const KalmanFilter& getKFr() const { return kf_r_; } + +private: + void initKF() { + kf_x_.A(0, 1) = s_sampling_time_; + kf_y_.A(0, 1) = s_sampling_time_; + kf_r_.A(0, 1) = s_sampling_time_; + + kf_x_.C(0, 0) = 1.0; + kf_y_.C(0, 0) = 1.0; + kf_r_.C(0, 0) = 1.0; + + kf_x_.R(0, 0) = s_measurement_variance_; + kf_y_.R(0, 0) = s_measurement_variance_; + kf_r_.R(0, 0) = s_measurement_variance_; + + kf_x_.Q(0, 0) = s_process_variance_; + kf_r_.Q(0, 0) = s_process_variance_; + kf_y_.Q(0, 0) = s_process_variance_; + + kf_x_.Q(1, 1) = s_process_rate_variance_; + kf_y_.Q(1, 1) = s_process_rate_variance_; + kf_r_.Q(1, 1) = s_process_rate_variance_; + + kf_x_.q_pred(0) = obstacle_.center.x; + kf_r_.q_pred(0) = obstacle_.radius; + kf_y_.q_pred(0) = obstacle_.center.y; + + kf_x_.q_pred(1) = obstacle_.velocity.x; + kf_y_.q_pred(1) = obstacle_.velocity.y; + + kf_x_.q_est(0) = obstacle_.center.x; + kf_r_.q_est(0) = obstacle_.radius; + kf_y_.q_est(0) = obstacle_.center.y; + + kf_x_.q_est(1) = obstacle_.velocity.x; + kf_y_.q_est(1) = obstacle_.velocity.y; + } + + CircleObstacle obstacle_; + + KalmanFilter kf_x_; + KalmanFilter kf_y_; + KalmanFilter kf_r_; + + int fade_counter_; + + // Common variables + static int s_fade_counter_size_; + static double s_sampling_time_; + static double s_process_variance_; + static double s_process_rate_variance_; + static double s_measurement_variance_; +}; + +} diff --git a/src/obstacle_detector/include/obstacle_detector/utilities/tracked_obstacle.h b/src/obstacle_detector/include/obstacle_detector/utilities/tracked_obstacle.h new file mode 100644 index 0000000..c0ca5d2 --- /dev/null +++ b/src/obstacle_detector/include/obstacle_detector/utilities/tracked_obstacle.h @@ -0,0 +1,7 @@ +#pragma once + +namespace obstacle_detector +{ + // Common variables + static uint64_t uid_next_ = 0; +}; diff --git a/src/obstacle_detector/include/obstacle_detector/utilities/tracked_segment_obstacle.h b/src/obstacle_detector/include/obstacle_detector/utilities/tracked_segment_obstacle.h new file mode 100644 index 0000000..73723ad --- /dev/null +++ b/src/obstacle_detector/include/obstacle_detector/utilities/tracked_segment_obstacle.h @@ -0,0 +1,207 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2017, Poznan University of Technology + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Poznan University of Technology nor the names + * of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Author: Mateusz Przybyla + */ + +#pragma once + +#include +#include "obstacle_detector/utilities/tracked_obstacle.h" +#include "obstacle_detector/utilities/kalman.h" + +namespace obstacle_detector +{ + +class TrackedSegmentObstacle { +public: + TrackedSegmentObstacle(const SegmentObstacle& obstacle) : obstacle_(obstacle), kf_x1_(0, 1, 2), kf_y1_(0, 1, 2), kf_x2_(0, 1, 2), kf_y2_(0, 1, 2) { + fade_counter_ = s_fade_counter_size_; + setNewUid(); + initKF(); + } + + void predictState() { + kf_x1_.predictState(); + kf_y1_.predictState(); + kf_x2_.predictState(); + kf_y2_.predictState(); + + obstacle_.first_point.x = kf_x1_.q_pred(0); + obstacle_.first_point.y = kf_y1_.q_pred(0); + obstacle_.last_point.x = kf_x2_.q_pred(0); + obstacle_.last_point.y = kf_y2_.q_pred(0); + + obstacle_.first_velocity.x = kf_x1_.q_pred(1); + obstacle_.first_velocity.y = kf_y1_.q_pred(1); + obstacle_.last_velocity.x = kf_x2_.q_pred(1); + obstacle_.last_velocity.y = kf_y2_.q_pred(1); + + fade_counter_--; + } + + void correctState(const SegmentObstacle& new_obstacle) { + kf_x1_.y(0) = new_obstacle.first_point.x; + kf_y1_.y(0) = new_obstacle.first_point.y; + kf_x2_.y(0) = new_obstacle.last_point.x; + kf_y2_.y(0) = new_obstacle.last_point.y; + + kf_x1_.correctState(); + kf_y1_.correctState(); + kf_x2_.correctState(); + kf_y2_.correctState(); + + obstacle_.first_point.x = kf_x1_.q_est(0); + obstacle_.first_point.y = kf_y1_.q_est(0); + obstacle_.last_point.x = kf_x2_.q_est(0); + obstacle_.last_point.y = kf_y2_.q_est(0); + + obstacle_.first_velocity.x = kf_x1_.q_est(1); + obstacle_.first_velocity.y = kf_y1_.q_est(1); + obstacle_.last_velocity.x = kf_x2_.q_est(1); + obstacle_.last_velocity.y = kf_y2_.q_est(1); + + fade_counter_ = s_fade_counter_size_; + } + + void updateState() { + kf_x1_.predictState(); + kf_y1_.predictState(); + kf_x2_.predictState(); + kf_y2_.predictState(); + + kf_x1_.correctState(); + kf_y1_.correctState(); + kf_x2_.correctState(); + kf_y2_.correctState(); + + obstacle_.first_point.x = kf_x1_.q_est(0); + obstacle_.first_point.y = kf_y1_.q_est(0); + obstacle_.last_point.x = kf_x2_.q_est(0); + obstacle_.last_point.y = kf_y2_.q_est(0); + + obstacle_.first_velocity.x = kf_x1_.q_est(1); + obstacle_.first_velocity.y = kf_y1_.q_est(1); + obstacle_.last_velocity.x = kf_x2_.q_est(1); + obstacle_.last_velocity.y = kf_y2_.q_est(1); + + fade_counter_--; + } + + static void setSamplingTime(double tp) { + s_sampling_time_ = tp; + } + + static void setCounterSize(int size) { + s_fade_counter_size_ = size; + } + + static void setCovariances(double process_var, double process_rate_var, double measurement_var) { + s_process_variance_ = process_var; + s_process_rate_variance_ = process_rate_var; + s_measurement_variance_ = measurement_var; + } + + void setNewUid() { obstacle_.uid = uid_next_++; } + bool hasFaded() const { return ((fade_counter_ <= 0) ? true : false); } + const SegmentObstacle& getObstacle() const { return obstacle_; } + const KalmanFilter& getKFx1() const { return kf_x1_; } + const KalmanFilter& getKFy1() const { return kf_y1_; } + const KalmanFilter& getKFx2() const { return kf_x2_; } + const KalmanFilter& getKFy2() const { return kf_y2_; } + +private: + void initKF() { + kf_x1_.A(0, 1) = s_sampling_time_; + kf_y1_.A(0, 1) = s_sampling_time_; + kf_x2_.A(0, 1) = s_sampling_time_; + kf_y2_.A(0, 1) = s_sampling_time_; + + kf_x1_.C(0, 0) = 1.0; + kf_y1_.C(0, 0) = 1.0; + kf_x2_.C(0, 0) = 1.0; + kf_y2_.C(0, 0) = 1.0; + + kf_x1_.R(0, 0) = s_measurement_variance_; + kf_y1_.R(0, 0) = s_measurement_variance_; + kf_x2_.R(0, 0) = s_measurement_variance_; + kf_y2_.R(0, 0) = s_measurement_variance_; + + kf_x1_.Q(0, 0) = s_process_variance_; + kf_y1_.Q(0, 0) = s_process_variance_; + kf_x2_.Q(0, 0) = s_process_variance_; + kf_y2_.Q(0, 0) = s_process_variance_; + + kf_x1_.Q(1, 1) = s_process_rate_variance_; + kf_y1_.Q(1, 1) = s_process_rate_variance_; + kf_x2_.Q(1, 1) = s_process_rate_variance_; + kf_y2_.Q(1, 1) = s_process_rate_variance_; + + kf_x1_.q_pred(0) = obstacle_.first_point.x; + kf_y1_.q_pred(0) = obstacle_.first_point.y; + kf_x2_.q_pred(0) = obstacle_.last_point.x; + kf_y2_.q_pred(0) = obstacle_.last_point.y; + + kf_x1_.q_pred(1) = obstacle_.first_velocity.x; + kf_y1_.q_pred(1) = obstacle_.first_velocity.y; + kf_x2_.q_pred(1) = obstacle_.last_velocity.x; + kf_y2_.q_pred(1) = obstacle_.last_velocity.y; + + kf_x1_.q_est(0) = obstacle_.first_point.x; + kf_y1_.q_est(0) = obstacle_.first_point.y; + kf_x2_.q_est(0) = obstacle_.last_point.x; + kf_y2_.q_est(0) = obstacle_.last_point.y; + + kf_x1_.q_est(1) = obstacle_.first_velocity.x; + kf_y1_.q_est(1) = obstacle_.first_velocity.y; + kf_x2_.q_est(1) = obstacle_.last_velocity.x; + kf_y2_.q_est(1) = obstacle_.last_velocity.y; + } + + SegmentObstacle obstacle_; + + KalmanFilter kf_x1_; + KalmanFilter kf_y1_; + KalmanFilter kf_x2_; + KalmanFilter kf_y2_; + + int fade_counter_; + + // Common variables + static int s_fade_counter_size_; + static double s_sampling_time_; + static double s_process_variance_; + static double s_process_rate_variance_; + static double s_measurement_variance_; +}; + +} diff --git a/src/obstacle_detector/launch/demo.launch b/src/obstacle_detector/launch/demo.launch new file mode 100644 index 0000000..5cebdbe --- /dev/null +++ b/src/obstacle_detector/launch/demo.launch @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + [-3.0, -2.5, -2.5, -1.0, -1.0, -0.5, 2.5, 0.2, 2.0, 4.5, 4.0, 1.5] + [1.5, 0.0, -2.5, 3.0, 1.0, -4.0, -3.0, -0.9, 0.0, 0.0, 2.0, 2.0] + [0.5, 0.5, 1.5, 0.5, 0.7, 0.5, 1.5, 0.7, 0.7, 1.0, 0.5, 1.0] + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] + + + + + + + + diff --git a/src/obstacle_detector/launch/nodelets.launch b/src/obstacle_detector/launch/nodelets.launch new file mode 100644 index 0000000..e06c15d --- /dev/null +++ b/src/obstacle_detector/launch/nodelets.launch @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + [-3.0, -2.5, -2.5, -1.0, -1.0, -0.5, 2.5, 0.2, 2.0, 4.5, 4.0, 1.5] + [1.5, 0.0, -2.5, 3.0, 1.0, -4.0, -3.0, -0.9, 0.0, 0.0, 2.0, 2.0] + [0.5, 0.5, 1.5, 0.5, 0.7, 0.5, 1.5, 0.7, 0.7, 1.0, 0.5, 1.0] + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] + + + + + + diff --git a/src/obstacle_detector/launch/nodes.launch b/src/obstacle_detector/launch/nodes.launch new file mode 100644 index 0000000..772a778 --- /dev/null +++ b/src/obstacle_detector/launch/nodes.launch @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/obstacle_detector/msg/CircleObstacle.msg b/src/obstacle_detector/msg/CircleObstacle.msg new file mode 100644 index 0000000..74e0b29 --- /dev/null +++ b/src/obstacle_detector/msg/CircleObstacle.msg @@ -0,0 +1,5 @@ +uint64 uid # Unique identifier +geometry_msgs/Point center # Central point [m] +geometry_msgs/Vector3 velocity # Linear velocity [m/s] +float64 radius # Radius with added margin [m] +float64 true_radius # True measured radius [m] diff --git a/src/obstacle_detector/msg/Obstacles.msg b/src/obstacle_detector/msg/Obstacles.msg new file mode 100644 index 0000000..ae63b42 --- /dev/null +++ b/src/obstacle_detector/msg/Obstacles.msg @@ -0,0 +1,4 @@ +Header header + +obstacle_detector/SegmentObstacle[] segments +obstacle_detector/CircleObstacle[] circles diff --git a/src/obstacle_detector/msg/SegmentObstacle.msg b/src/obstacle_detector/msg/SegmentObstacle.msg new file mode 100644 index 0000000..90c2627 --- /dev/null +++ b/src/obstacle_detector/msg/SegmentObstacle.msg @@ -0,0 +1,5 @@ +uint64 uid # Unique identifier +geometry_msgs/Point first_point # First point of the segment [m] +geometry_msgs/Point last_point # Last point of the segment [m] +geometry_msgs/Vector3 first_velocity # Linear velocity [m/s] +geometry_msgs/Vector3 last_velocity # Linear velocity [m/s] diff --git a/src/obstacle_detector/nodelet_plugins.xml b/src/obstacle_detector/nodelet_plugins.xml new file mode 100644 index 0000000..63550af --- /dev/null +++ b/src/obstacle_detector/nodelet_plugins.xml @@ -0,0 +1,35 @@ + + + + + Scans Merger Nodelet + + + + + + Obstacle Extractor Nodelet + + + + + + Obstacle Tracker Nodelet + + + + + + Obstacle Publisher Nodelet + + + + diff --git a/src/obstacle_detector/package.xml b/src/obstacle_detector/package.xml new file mode 100644 index 0000000..e2d78e5 --- /dev/null +++ b/src/obstacle_detector/package.xml @@ -0,0 +1,31 @@ + + + obstacle_detector + 1.0.0 + Detect obstacles in form of line segments and circles from 2D laser scans. + Mateusz Przybyla + Mateusz Przybyla + BSD + + catkin + message_generation + message_runtime + + tf + rviz + roscpp + roslaunch + nodelet + std_msgs + std_srvs + sensor_msgs + geometry_msgs + laser_geometry + armadillo + nav_msgs + + + + + + diff --git a/src/obstacle_detector/resources/ObstacleDetector.pdf b/src/obstacle_detector/resources/ObstacleDetector.pdf new file mode 100644 index 0000000..a284f2b Binary files /dev/null and b/src/obstacle_detector/resources/ObstacleDetector.pdf differ diff --git a/src/obstacle_detector/resources/Using hokuyo_node with udev.txt b/src/obstacle_detector/resources/Using hokuyo_node with udev.txt new file mode 100644 index 0000000..22ee924 --- /dev/null +++ b/src/obstacle_detector/resources/Using hokuyo_node with udev.txt @@ -0,0 +1,36 @@ +How to use multiple Hokuyo laser scanners and determine which is which? +---- + +When you plug in the usb Hokuyo laser scanner into computer running Ubuntu, it shows up as a link in /dev/ folders (e.g. /dev/ttyACM0). If you have two or more laser scanners connected, you cannot be sure which link is dedicated to which device. In order to make sure that the Hokuyo laser scanners connected to the computer are being properly identified, one can make use of the getID node provided by the hokuyo_node package and the udev rules. To do so, take the following steps: + +1. Create a file with an udev rule in the folder /etc/udev/rules.d: + +sudo gedit /etc/udev/rules.d/47-hokuyo.rules + +(the name and number is arbitrary but there should not be two rules with the same number). + +2. Fill the file with the following rule (just paste the following in the file): + +SUBSYSTEMS=="usb", KERNEL=="ttyACM[0-9]*", ACTION=="add", ATTRS{idVendor}=="15d1", ATTRS{idProduct}=="0000", ATTRS{manufacturer}=="Hokuyo Data Flex for USB", ATTRS{product}=="URG-Series USB Driver", MODE="666", PROGRAM="/etc/ros/run.sh hokuyo_node getID %N q", SYMLINK+="sensors/hokuyo_%c", GROUP="dialout" + +(mind that the user should be a member of dialout group: sudo adduser $USER dialout). + +3. Create a file named run.sh in the /etc/ros/ folder and provide it with executable rights: + +sudo touch /etc/ros/run.sh +sudo chmod +x /etc/ros/run.sh + +4. Fill the file with the following: + +#!/bin/sh +. /opt/ros/hydro/setup.sh +rosrun $@ + +(change the distribution of ROS to the one that you use - it was Hydromedusa in my case). + +5. Refresh the udev rules list with: + +sudo udevadm control --reload-rules + +From now on, after plugging in the usb Hokuyo laser scanner, the /dev/ folder should not only contain ttyACMn links, but also /sensors/hokuyo_ links, with which you can unambiguously determine which device are you using. Good luck! + diff --git a/src/obstacle_detector/resources/obstacle_detector.rviz b/src/obstacle_detector/resources/obstacle_detector.rviz new file mode 100644 index 0000000..cdf288c --- /dev/null +++ b/src/obstacle_detector/resources/obstacle_detector.rviz @@ -0,0 +1,269 @@ +Panels: + - Class: rviz/Displays + Help Height: 0 + Name: Displays + Property Tree Widget: + Expanded: ~ + Splitter Ratio: 0.5 + Tree Height: 931 + - Class: rviz/Selection + Name: Selection + - Class: rviz/Views + Expanded: + - /Current View1 + Name: Views + Splitter Ratio: 0.5 + - Class: rviz/Time + Experimental: false + Name: Time + SyncMode: 0 + SyncSource: Merged PCL + - Class: Scans Merger + Name: Scans Merger + - Class: Obstacle Extractor + Name: Obstacle Extractor + - Class: Obstacle Tracker + Name: Obstacle Tracker + - Class: Obstacle Publisher + Name: Obstacle Publisher +Visualization Manager: + Class: "" + Displays: + - Alpha: 0.5 + Cell Size: 1 + Class: rviz/Grid + Color: 160; 160; 164 + Enabled: true + Line Style: + Line Width: 0.0299999993 + Value: Lines + Name: Grid + Normal Cell Count: 0 + Offset: + X: 0 + Y: 0 + Z: 0 + Plane: XY + Plane Cell Count: 10 + Reference Frame: map + Value: true + - Alpha: 1 + Autocompute Intensity Bounds: true + Autocompute Value Bounds: + Max Value: 10 + Min Value: -10 + Value: true + Axis: Z + Channel Name: intensity + Class: rviz/LaserScan + Color: 255; 0; 0 + Color Transformer: FlatColor + Decay Time: 0 + Enabled: false + Invert Rainbow: false + Max Color: 255; 255; 255 + Max Intensity: 4096 + Min Color: 0; 0; 0 + Min Intensity: 0 + Name: Front Scanner + Position Transformer: XYZ + Queue Size: 10 + Selectable: false + Size (Pixels): 3 + Size (m): 0.00999999978 + Style: Points + Topic: /front_scan + Unreliable: true + Use Fixed Frame: true + Use rainbow: true + Value: false + - Alpha: 1 + Autocompute Intensity Bounds: true + Autocompute Value Bounds: + Max Value: 10 + Min Value: -10 + Value: true + Axis: Z + Channel Name: intensity + Class: rviz/LaserScan + Color: 255; 255; 255 + Color Transformer: FlatColor + Decay Time: 0 + Enabled: false + Invert Rainbow: false + Max Color: 255; 255; 255 + Max Intensity: 4096 + Min Color: 0; 0; 0 + Min Intensity: 0 + Name: Rear Scanner + Position Transformer: XYZ + Queue Size: 10 + Selectable: false + Size (Pixels): 3 + Size (m): 0.00999999978 + Style: Points + Topic: /rear_scan + Unreliable: true + Use Fixed Frame: true + Use rainbow: true + Value: false + - Alpha: 1 + Autocompute Intensity Bounds: true + Autocompute Value Bounds: + Max Value: 10 + Min Value: -10 + Value: true + Axis: Z + Channel Name: intensity + Class: rviz/LaserScan + Color: 85; 170; 255 + Color Transformer: FlatColor + Decay Time: 0 + Enabled: true + Invert Rainbow: false + Max Color: 255; 255; 255 + Max Intensity: 4096 + Min Color: 0; 0; 0 + Min Intensity: 0 + Name: Merged Scan + Position Transformer: XYZ + Queue Size: 10 + Selectable: false + Size (Pixels): 5 + Size (m): 0.00999999978 + Style: Points + Topic: /scan + Unreliable: true + Use Fixed Frame: true + Use rainbow: true + Value: true + - Alpha: 1 + Autocompute Intensity Bounds: true + Autocompute Value Bounds: + Max Value: 10 + Min Value: -10 + Value: true + Axis: Z + Channel Name: intensity + Class: rviz/PointCloud + Color: 85; 170; 255 + Color Transformer: FlatColor + Decay Time: 0 + Enabled: true + Invert Rainbow: false + Max Color: 255; 255; 255 + Max Intensity: 4096 + Min Color: 0; 0; 0 + Min Intensity: 0 + Name: Merged PCL + Position Transformer: XYZ + Queue Size: 10 + Selectable: false + Size (Pixels): 5 + Size (m): 0.00999999978 + Style: Points + Topic: /pcl + Unreliable: true + Use Fixed Frame: true + Use rainbow: true + Value: true + - Circles color: 170; 0; 0 + Class: Obstacles + Enabled: true + Margin color: 0; 170; 0 + Name: Obstacles + Opacity: 0.75 + Segments color: 170; 170; 0 + Segments thickness: 0.0299999993 + Topic: /obstacles + Unreliable: true + Value: true + - Class: rviz/TF + Enabled: true + Frame Timeout: 15 + Frames: + All Enabled: true + front_scanner: + Value: true + map: + Value: true + rear_scanner: + Value: true + robot: + Value: true + Marker Scale: 1 + Name: TF + Show Arrows: true + Show Axes: true + Show Names: true + Tree: + map: + robot: + front_scanner: + {} + rear_scanner: + {} + Update Interval: 0 + Value: true + Enabled: true + Global Options: + Background Color: 48; 48; 48 + Fixed Frame: map + Frame Rate: 30 + Name: root + Tools: + - Class: rviz/Interact + Hide Inactive Objects: true + - Class: rviz/MoveCamera + - Class: rviz/Select + - Class: rviz/FocusCamera + - Class: rviz/Measure + - Class: rviz/SetInitialPose + Topic: /initialpose + - Class: rviz/SetGoal + Topic: /move_base_simple/goal + - Class: rviz/PublishPoint + Single click: true + Topic: /clicked_point + Value: true + Views: + Current: + Angle: 0 + Class: rviz/TopDownOrtho + Enable Stereo Rendering: + Stereo Eye Separation: 0.0599999987 + Stereo Focal Distance: 1 + Swap Stereo Eyes: false + Value: false + Name: Current View + Near Clip Distance: 0.00999999978 + Scale: 77.9355011 + Target Frame: map + Value: TopDownOrtho (rviz) + X: 0 + Y: 0 + Saved: ~ +Window Geometry: + Displays: + collapsed: false + Height: 1056 + Hide Left Dock: false + Hide Right Dock: false + Obstacle Extractor: + collapsed: false + Obstacle Publisher: + collapsed: false + Obstacle Tracker: + collapsed: false + QMainWindow State: 000000ff00000000fd00000004000000000000016a00000402fc0200000008fb0000001200530065006c0065006300740069006f006e00000001e10000009b0000006400fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000001ed000001df00000185000000a3fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afb0000002000730065006c0065006300740069006f006e00200062007500660066006500720200000138000000aa0000023a00000294fb00000014005700690064006500530074006500720065006f02000000e6000000d2000003ee0000030bfb0000000c004b0069006e0065006300740200000186000001060000030c00000261fc0000000000000402000002010100001dfa000000000100000004fb00000018005300630061006e00730020004d006500720067006500720100000000ffffffff0000011900fffffffb00000024004f00620073007400610063006c006500200045007800740072006100630074006f00720100000000ffffffff0000013600fffffffb00000020004f00620073007400610063006c006500200054007200610063006b006500720100000000ffffffff0000013b00fffffffb00000022004f00620073007400610063006c00650020004400650074006500630074006f00720100000000ffffffff0000000000000000000000010000016a00000402fc0200000004fb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fb00000022004f00620073007400610063006c00650020005200650063006f00720064006500720000000000000000d70000000000000000fc0000000000000402000001dd0100001dfa000000020100000003fb0000000a005600690065007700730100000000ffffffff0000010f00fffffffb000000100044006900730070006c0061007900730100000000ffffffff0000016a00fffffffb00000024004f00620073007400610063006c00650020005000750062006c0069007300680065007201000003ab0000016a0000016600fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e100000197000000030000073f0000003efc0100000002fb0000000800540069006d006500000000000000073f0000030000fffffffb0000000800540069006d006501000000000000045000000000000000000000045f0000040200000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730000000000ffffffff0000000000000000 + Scans Merger: + collapsed: false + Selection: + collapsed: false + Time: + collapsed: false + Views: + collapsed: false + Width: 1855 + X: 65 + Y: 24 diff --git a/src/obstacle_detector/resources/play.png b/src/obstacle_detector/resources/play.png new file mode 100644 index 0000000..1a7d96a Binary files /dev/null and b/src/obstacle_detector/resources/play.png differ diff --git a/src/obstacle_detector/resources/scans_demo.bag b/src/obstacle_detector/resources/scans_demo.bag new file mode 100644 index 0000000..27c2fc8 Binary files /dev/null and b/src/obstacle_detector/resources/scans_demo.bag differ diff --git a/src/obstacle_detector/resources/stop.png b/src/obstacle_detector/resources/stop.png new file mode 100644 index 0000000..dbdbb34 Binary files /dev/null and b/src/obstacle_detector/resources/stop.png differ diff --git a/src/obstacle_detector/rviz_plugins.xml b/src/obstacle_detector/rviz_plugins.xml new file mode 100644 index 0000000..bfd46f4 --- /dev/null +++ b/src/obstacle_detector/rviz_plugins.xml @@ -0,0 +1,44 @@ + + + + + Scans Merger Panel. + + + + + + Obstacle Extractor Panel. + + + + + + Obstacle Tracker Panel. + + + + + + Obstacle Publisher Panel. + + + + + + Obstacles Display + + obstacle_detector/Obstacles + + + diff --git a/src/obstacle_detector/src/displays/circle_visual.cpp b/src/obstacle_detector/src/displays/circle_visual.cpp new file mode 100644 index 0000000..033f66b --- /dev/null +++ b/src/obstacle_detector/src/displays/circle_visual.cpp @@ -0,0 +1,113 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2017, Poznan University of Technology + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Poznan University of Technology nor the names + * of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Author: Mateusz Przybyla + */ + +#include "obstacle_detector/displays/circle_visual.h" + +namespace obstacles_display +{ + +CircleVisual::CircleVisual(Ogre::SceneManager* scene_manager, Ogre::SceneNode* parent_node) { + scene_manager_ = scene_manager; + frame_node_obstacle_ = parent_node->createChildSceneNode(); + frame_node_margin_ = parent_node->createChildSceneNode(); + frame_node_velocity_ = parent_node->createChildSceneNode(); + frame_node_text_ = parent_node->createChildSceneNode(); + + obstacle_.reset(new rviz::Shape(rviz::Shape::Cylinder, scene_manager_, frame_node_obstacle_)); + margin_.reset(new rviz::Shape(rviz::Shape::Cylinder, scene_manager_, frame_node_margin_)); + velocity_.reset(new rviz::Arrow(scene_manager_, frame_node_margin_)); + const auto color = Ogre::ColourValue(1.0f, 0.5f, 0.0f); + velocity_->setColor(color); + center_position_ = Ogre::Vector3(0.0, 0.0, 0.0); + text_ = new rviz::MovableText("Circle"); + text_->setTextAlignment(rviz::MovableText::H_CENTER, rviz::MovableText::V_CENTER); + frame_node_text_->attachObject(text_); +} + +CircleVisual::~CircleVisual() { + scene_manager_->destroySceneNode(frame_node_obstacle_); + scene_manager_->destroySceneNode(frame_node_margin_); + scene_manager_->destroySceneNode(frame_node_velocity_); + scene_manager_->destroySceneNode(frame_node_text_); + delete text_; +} + +void CircleVisual::setData(const obstacle_detector::CircleObstacle& circle) { + center_position_ = Ogre::Vector3(circle.center.x, circle.center.y, 0.25); + obstacle_->setPosition(center_position_); + + Ogre::Vector3 true_scale(2.0 * circle.true_radius, 0.1, 2.0 * circle.true_radius); + obstacle_->setScale(true_scale); + + Ogre::Vector3 pos2(circle.center.x, circle.center.y, 0.1); + margin_->setPosition(pos2); + + Ogre::Vector3 scale(2.0 * circle.radius, 0.2, 2.0 * circle.radius); + margin_->setScale(scale); + + const auto speed = sqrt(pow(circle.velocity.x, 2.0) + pow(circle.velocity.y, 2.0)); + velocity_->setPosition(center_position_); + velocity_->setDirection(Ogre::Vector3(circle.velocity.x, circle.velocity.y, circle.velocity.z)); + velocity_->setScale(Ogre::Vector3(speed)); + + text_->setCharacterHeight(std::max(0.1, circle.true_radius * 2)); + text_->setCaption(std::to_string(circle.uid)); + + Ogre::Vector3 dir(Ogre::Real(1.0), Ogre::Real(0.0), Ogre::Real(0.0)); + Ogre::Radian angle(Ogre::Real(M_PI_2)); + Ogre::Quaternion q(angle, dir); + obstacle_->setOrientation(q); + margin_->setOrientation(q); +} + +void CircleVisual::setFramePose(const Ogre::Vector3& position, const Ogre::Quaternion& orientation) { + frame_node_obstacle_->setPosition(position); + frame_node_margin_->setPosition(position); + frame_node_text_->setPosition(position + orientation * center_position_); + frame_node_obstacle_->setOrientation(orientation); + frame_node_margin_->setOrientation(orientation); + //frame_node_text_->setOrientation(orientation); // Has no effect +} + +void CircleVisual::setMainColor(float r, float g, float b, float a) { + obstacle_->setColor(r, g, b, a); +} + +void CircleVisual::setMarginColor(float r, float g, float b, float a) { + margin_->setColor(r, g, b, a); +} + +} // end namespace obstacles_display + diff --git a/src/obstacle_detector/src/displays/obstacles_display.cpp b/src/obstacle_detector/src/displays/obstacles_display.cpp new file mode 100644 index 0000000..3366d5b --- /dev/null +++ b/src/obstacle_detector/src/displays/obstacles_display.cpp @@ -0,0 +1,131 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2017, Poznan University of Technology + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Poznan University of Technology nor the names + * of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Author: Mateusz Przybyla + */ + +#include "obstacle_detector/displays/obstacles_display.h" + +namespace obstacles_display +{ + +ObstaclesDisplay::ObstaclesDisplay() { + circle_color_property_ = new rviz::ColorProperty("Circles color", QColor(170, 0, 0), "Color of circles.", this, SLOT(updateCircleColor())); + margin_color_property_ = new rviz::ColorProperty("Margin color", QColor(0, 170, 0), "Color of margin added around circles.", this, SLOT(updateCircleColor())); + segment_color_property_ = new rviz::ColorProperty("Segments color", QColor(170, 170, 0), "Color of segments.", this, SLOT(updateSegmentColor())); + alpha_property_ = new rviz::FloatProperty("Opacity", 0.75, "Value 0,0 is fully transparent, 1,0 is fully opaque.", this, SLOT(updateAlpha())); + thickness_property_ = new rviz::FloatProperty("Segments thickness", 0.03f, "Width of the segments in meters.", this, SLOT(updateThickness())); +} + +void ObstaclesDisplay::onInitialize() { + MessageFilterDisplay::onInitialize(); +} + +ObstaclesDisplay::~ObstaclesDisplay() {} + +void ObstaclesDisplay::reset() { + MessageFilterDisplay::reset(); + circle_visuals_.clear(); + segment_visuals_.clear(); +} + +void ObstaclesDisplay::updateCircleColor() { + float alpha = alpha_property_->getFloat(); + Ogre::ColourValue main_color = circle_color_property_->getOgreColor(); + Ogre::ColourValue margin_color = margin_color_property_->getOgreColor(); + + for (auto& c : circle_visuals_) { + c->setMainColor(main_color.r, main_color.g, main_color.b, alpha); + c->setMarginColor(margin_color.r, margin_color.g, margin_color.b, alpha); + } +} + +void ObstaclesDisplay::updateSegmentColor() { + float alpha = alpha_property_->getFloat(); + Ogre::ColourValue color = segment_color_property_->getOgreColor(); + + for (auto& s : segment_visuals_) + s->setColor(color.r, color.g, color.b, alpha); +} + +void ObstaclesDisplay::updateAlpha() { + updateCircleColor(); + updateSegmentColor(); +} + +void ObstaclesDisplay::updateThickness() { + float width = thickness_property_->getFloat(); + + for (auto& s : segment_visuals_) + s->setWidth(width); +} + +void ObstaclesDisplay::processMessage(const obstacle_detector::Obstacles::ConstPtr& obstacles_msg) { + circle_visuals_.clear(); + segment_visuals_.clear(); + + Ogre::Quaternion orientation; + Ogre::Vector3 position; + if (!context_->getFrameManager()->getTransform(obstacles_msg->header.frame_id, obstacles_msg->header.stamp, position, orientation)) { + ROS_DEBUG( "Error transforming from frame '%s' to frame '%s'", obstacles_msg->header.frame_id.c_str(), qPrintable( fixed_frame_ )); + return; + } + + for (const auto& circle : obstacles_msg->circles) { + boost::shared_ptr visual; + visual.reset(new CircleVisual(context_->getSceneManager(), scene_node_)); + + visual->setData(circle); + visual->setFramePose(position, orientation); + + circle_visuals_.push_back(visual); + } + + for (const auto& segment : obstacles_msg->segments) { + boost::shared_ptr visual; + visual.reset(new SegmentVisual(context_->getSceneManager(), scene_node_)); + + visual->setData(segment); + visual->setFramePose(position, orientation); + + segment_visuals_.push_back(visual); + } + + updateAlpha(); + updateThickness(); +} + +} // end namespace obstacles_display + +#include +PLUGINLIB_EXPORT_CLASS(obstacles_display::ObstaclesDisplay, rviz::Display) + diff --git a/src/obstacle_detector/src/displays/segment_visual.cpp b/src/obstacle_detector/src/displays/segment_visual.cpp new file mode 100644 index 0000000..5d30059 --- /dev/null +++ b/src/obstacle_detector/src/displays/segment_visual.cpp @@ -0,0 +1,99 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2017, Poznan University of Technology + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Poznan University of Technology nor the names + * of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Author: Mateusz Przybyla + */ + +#include "obstacle_detector/displays/segment_visual.h" + +namespace obstacles_display +{ + +SegmentVisual::SegmentVisual(Ogre::SceneManager* scene_manager, Ogre::SceneNode* parent_node) { + scene_manager_ = scene_manager; + frame_node_line_ = parent_node->createChildSceneNode(); + frame_node_velocity_ = parent_node->createChildSceneNode(); + frame_node_text_ = parent_node->createChildSceneNode(); + + line_.reset(new rviz::BillboardLine(scene_manager_, frame_node_line_)); + velocity_.reset(new rviz::Arrow(scene_manager_, frame_node_line_)); + const auto color = Ogre::ColourValue(0.0f, 0.5f, 1.0f); + velocity_->setColor(color); + center_position_ = Ogre::Vector3(0.0, 0.0, 0.0); + text_ = new rviz::MovableText("Segment"); + text_->setTextAlignment(rviz::MovableText::H_CENTER, rviz::MovableText::V_CENTER); + text_->setColor(color); + frame_node_text_->attachObject(text_); +} + +SegmentVisual::~SegmentVisual() { + scene_manager_->destroySceneNode(frame_node_line_); + scene_manager_->destroySceneNode(frame_node_velocity_); + scene_manager_->destroySceneNode(frame_node_text_); + delete text_; +} + +void SegmentVisual::setData(const obstacle_detector::SegmentObstacle& segment) { + Ogre::Vector3 p1(segment.first_point.x, segment.first_point.y, 0.0); + Ogre::Vector3 p2(segment.last_point.x, segment.last_point.y, 0.0); + line_->addPoint(p1); + line_->addPoint(p2); + + center_position_ = (p1 + p2) / 2; + const auto velocity = Ogre::Vector3(segment.first_velocity.x + segment.last_velocity.x, segment.first_velocity.y + segment.last_velocity.y, segment.first_velocity.z + segment.last_velocity.z); + const auto speed = sqrt(pow(velocity.x, 2.0) + pow(velocity.y, 2.0)); + velocity_->setPosition(center_position_); + velocity_->setDirection(velocity); + velocity_->setScale(Ogre::Vector3(speed)); + + //text_->setLocalTranslation(Ogre::Vector3(0.5,0,0)); + text_->setCharacterHeight(std::max(0.1, std::min(0.5, sqrt(pow(p1.x - p2.x, 2.0) + pow(p1.y - p2.y, 2.0)) / 2.0))); + text_->setCaption(std::to_string(segment.uid)); +} + +void SegmentVisual::setFramePose(const Ogre::Vector3& position, const Ogre::Quaternion& orientation) { + frame_node_line_->setPosition(position); + frame_node_text_->setPosition(position + orientation * center_position_); + frame_node_line_->setOrientation(orientation); + //frame_node_text_->setOrientation(orientation); // Has no effect +} + +void SegmentVisual::setColor(float r, float g, float b, float a) { + line_->setColor(r, g, b, a); +} + +void SegmentVisual::setWidth(float w) { + line_->setLineWidth(w); +} + +} // end namespace obstacles_display + diff --git a/src/obstacle_detector/src/nodelets/obstacle_extractor_nodelet.cpp b/src/obstacle_detector/src/nodelets/obstacle_extractor_nodelet.cpp new file mode 100644 index 0000000..617d2b3 --- /dev/null +++ b/src/obstacle_detector/src/nodelets/obstacle_extractor_nodelet.cpp @@ -0,0 +1,74 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2017, Poznan University of Technology + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Poznan University of Technology nor the names + * of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Author: Mateusz Przybyla + */ + +#include +#include + +#include "obstacle_detector/obstacle_extractor.h" + +namespace obstacle_detector +{ + +class ObstacleExtractorNodelet : public nodelet::Nodelet +{ +public: + virtual void onInit() { + ros::NodeHandle nh = getNodeHandle(); + ros::NodeHandle nh_local = getPrivateNodeHandle(); + + try { + NODELET_INFO("[Obstacle Extractor]: Initializing nodelet"); + obstacle_extractor_ = std::shared_ptr(new ObstacleExtractor(nh, nh_local)); + } + catch (const char* s) { + NODELET_FATAL_STREAM("[Obstacle Extractor]: " << s); + } + catch (...) { + NODELET_FATAL_STREAM("[Obstacle Extractor]: Unexpected error"); + } + } + + virtual ~ObstacleExtractorNodelet() { + NODELET_INFO("[Obstacle Extractor]: Shutdown"); + } + +private: + std::shared_ptr obstacle_extractor_; +}; + +} // namespace obstacle_detector + +#include +PLUGINLIB_EXPORT_CLASS(obstacle_detector::ObstacleExtractorNodelet, nodelet::Nodelet) diff --git a/src/obstacle_detector/src/nodelets/obstacle_publisher_nodelet.cpp b/src/obstacle_detector/src/nodelets/obstacle_publisher_nodelet.cpp new file mode 100644 index 0000000..270101b --- /dev/null +++ b/src/obstacle_detector/src/nodelets/obstacle_publisher_nodelet.cpp @@ -0,0 +1,74 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2017, Poznan University of Technology + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Poznan University of Technology nor the names + * of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Author: Mateusz Przybyla + */ + +#include +#include + +#include "obstacle_detector/obstacle_publisher.h" + +namespace obstacle_detector +{ + +class ObstaclePublisherNodelet : public nodelet::Nodelet +{ +public: + virtual void onInit() { + ros::NodeHandle nh = getNodeHandle(); + ros::NodeHandle nh_local = getPrivateNodeHandle(); + + try { + NODELET_INFO("[Obstacle Publisher]: Initializing nodelet"); + obstacle_publisher_ = std::shared_ptr(new ObstaclePublisher(nh, nh_local)); + } + catch (const char* s) { + NODELET_FATAL_STREAM("[Obstacle Publisher]: " << s); + } + catch (...) { + NODELET_FATAL_STREAM("[Obstacle Publisher]: Unexpected error"); + } + } + + virtual ~ObstaclePublisherNodelet() { + NODELET_INFO("[Obstacle Publisher]: Shutdown"); + } + +private: + std::shared_ptr obstacle_publisher_; +}; + +} // namespace obstacle_detector + +#include +PLUGINLIB_EXPORT_CLASS(obstacle_detector::ObstaclePublisherNodelet, nodelet::Nodelet) diff --git a/src/obstacle_detector/src/nodelets/obstacle_tracker_nodelet.cpp b/src/obstacle_detector/src/nodelets/obstacle_tracker_nodelet.cpp new file mode 100644 index 0000000..78244a3 --- /dev/null +++ b/src/obstacle_detector/src/nodelets/obstacle_tracker_nodelet.cpp @@ -0,0 +1,74 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2017, Poznan University of Technology + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Poznan University of Technology nor the names + * of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Author: Mateusz Przybyla + */ + +#include +#include + +#include "obstacle_detector/obstacle_tracker.h" + +namespace obstacle_detector +{ + +class ObstacleTrackerNodelet : public nodelet::Nodelet +{ +public: + virtual void onInit() { + ros::NodeHandle nh = getNodeHandle(); + ros::NodeHandle nh_local = getPrivateNodeHandle(); + + try { + NODELET_INFO("[Obstacle Tracker]: Initializing nodelet"); + obstacle_tracker_ = std::shared_ptr(new ObstacleTracker(nh, nh_local)); + } + catch (const char* s) { + NODELET_FATAL_STREAM("[Obstacle Tracker]: " << s); + } + catch (...) { + NODELET_FATAL_STREAM("[Obstacle Tracker]: Unexpected error"); + } + } + + virtual ~ObstacleTrackerNodelet() { + NODELET_INFO("[Obstacle Tracker]: Shutdown"); + } + +private: + std::shared_ptr obstacle_tracker_; +}; + +} // namespace obstacle_detector + +#include +PLUGINLIB_EXPORT_CLASS(obstacle_detector::ObstacleTrackerNodelet, nodelet::Nodelet) diff --git a/src/obstacle_detector/src/nodelets/scans_merger_nodelet.cpp b/src/obstacle_detector/src/nodelets/scans_merger_nodelet.cpp new file mode 100644 index 0000000..a9fa11c --- /dev/null +++ b/src/obstacle_detector/src/nodelets/scans_merger_nodelet.cpp @@ -0,0 +1,74 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2017, Poznan University of Technology + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Poznan University of Technology nor the names + * of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Author: Mateusz Przybyla + */ + +#include +#include + +#include "obstacle_detector/scans_merger.h" + +namespace obstacle_detector +{ + +class ScansMergerNodelet : public nodelet::Nodelet +{ +public: + virtual void onInit() { + ros::NodeHandle nh = getNodeHandle(); + ros::NodeHandle nh_local = getPrivateNodeHandle(); + + try { + NODELET_INFO("[Scans Merger]: Initializing nodelet"); + scans_merger_ = std::shared_ptr(new ScansMerger(nh, nh_local)); + } + catch (const char* s) { + NODELET_FATAL_STREAM("[Scans Merger]: " << s); + } + catch (...) { + NODELET_FATAL_STREAM("[Scans Merger]: Unexpected error"); + } + } + + virtual ~ScansMergerNodelet() { + NODELET_INFO("[Scans Merger]: Shutdown"); + } + +private: + std::shared_ptr scans_merger_; +}; + +} // namespace obstacle_detector + +#include +PLUGINLIB_EXPORT_CLASS(obstacle_detector::ScansMergerNodelet, nodelet::Nodelet) diff --git a/src/obstacle_detector/src/nodes/obstacle_extractor_node.cpp b/src/obstacle_detector/src/nodes/obstacle_extractor_node.cpp new file mode 100644 index 0000000..09c95bc --- /dev/null +++ b/src/obstacle_detector/src/nodes/obstacle_extractor_node.cpp @@ -0,0 +1,58 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2017, Poznan University of Technology + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Poznan University of Technology nor the names + * of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Author: Mateusz Przybyla + */ + +#include "obstacle_detector/obstacle_extractor.h" + +using namespace obstacle_detector; + +int main(int argc, char** argv) { + ros::init(argc, argv, "obstacle_extractor", ros::init_options::NoRosout); + ros::NodeHandle nh(""); + ros::NodeHandle nh_local("~"); + + try { + ROS_INFO("[Obstacle Extractor]: Initializing node"); + ObstacleExtractor od(nh, nh_local); + ros::spin(); + } + catch (const char* s) { + ROS_FATAL_STREAM("[Obstacle Extractor]: " << s); + } + catch (...) { + ROS_FATAL_STREAM("[Obstacle Extractor]: Unexpected error"); + } + + return 0; +} diff --git a/src/obstacle_detector/src/nodes/obstacle_publisher_node.cpp b/src/obstacle_detector/src/nodes/obstacle_publisher_node.cpp new file mode 100644 index 0000000..6ff875a --- /dev/null +++ b/src/obstacle_detector/src/nodes/obstacle_publisher_node.cpp @@ -0,0 +1,58 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2017, Poznan University of Technology + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Poznan University of Technology nor the names + * of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Author: Mateusz Przybyla + */ + +#include "obstacle_detector/obstacle_publisher.h" + +using namespace obstacle_detector; + +int main(int argc, char** argv) { + ros::init(argc, argv, "obstacle_publisher", ros::init_options::NoRosout); + ros::NodeHandle nh(""); + ros::NodeHandle nh_local("~"); + + try { + ROS_INFO("[Obstacle Publisher]: Initializing node"); + ObstaclePublisher op(nh, nh_local); + ros::spin(); + } + catch (const char* s) { + ROS_FATAL_STREAM("[Obstacle Publisher]: " << s); + } + catch (...) { + ROS_FATAL_STREAM("[Obstacle Publisher]: Unexpected error"); + } + + return 0; +} diff --git a/src/obstacle_detector/src/nodes/obstacle_tracker_node.cpp b/src/obstacle_detector/src/nodes/obstacle_tracker_node.cpp new file mode 100644 index 0000000..efb9400 --- /dev/null +++ b/src/obstacle_detector/src/nodes/obstacle_tracker_node.cpp @@ -0,0 +1,58 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2017, Poznan University of Technology + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Poznan University of Technology nor the names + * of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Author: Mateusz Przybyla + */ + +#include "obstacle_detector/obstacle_tracker.h" + +using namespace obstacle_detector; + +int main(int argc, char** argv) { + ros::init(argc, argv, "obstacle_tracker", ros::init_options::NoRosout); + ros::NodeHandle nh(""); + ros::NodeHandle nh_local("~"); + + try { + ROS_INFO("[Obstacle Tracker]: Initializing node"); + ObstacleTracker ot(nh, nh_local); + ros::spin(); + } + catch (const char* s) { + ROS_FATAL_STREAM("[Obstacle Tracker]: " << s); + } + catch (...) { + ROS_FATAL_STREAM("[Obstacle Tracker]: Unexpected error"); + } + + return 0; +} diff --git a/src/obstacle_detector/src/nodes/scans_merger_node.cpp b/src/obstacle_detector/src/nodes/scans_merger_node.cpp new file mode 100644 index 0000000..19c08e2 --- /dev/null +++ b/src/obstacle_detector/src/nodes/scans_merger_node.cpp @@ -0,0 +1,58 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2017, Poznan University of Technology + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Poznan University of Technology nor the names + * of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Author: Mateusz Przybyla + */ + +#include "obstacle_detector/scans_merger.h" + +using namespace obstacle_detector; + +int main(int argc, char** argv) { + ros::init(argc, argv, "scans_merger", ros::init_options::NoRosout); + ros::NodeHandle nh(""); + ros::NodeHandle nh_local("~"); + + try { + ROS_INFO("[Scans Merger]: Initializing node"); + ScansMerger sm(nh, nh_local); + ros::spin(); + } + catch (const char* s) { + ROS_FATAL_STREAM("[Scans Merger]: " << s); + } + catch (...) { + ROS_FATAL_STREAM("[Scans Merger]: Unexpected error"); + } + + return 0; +} diff --git a/src/obstacle_detector/src/obstacle_extractor.cpp b/src/obstacle_detector/src/obstacle_extractor.cpp new file mode 100755 index 0000000..39f11cd --- /dev/null +++ b/src/obstacle_detector/src/obstacle_extractor.cpp @@ -0,0 +1,484 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2017, Poznan University of Technology + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Poznan University of Technology nor the names + * of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Author: Mateusz Przybyla + */ + +#include "obstacle_detector/obstacle_extractor.h" +#include "obstacle_detector/utilities/figure_fitting.h" +#include "obstacle_detector/utilities/math_utilities.h" + +using namespace std; +using namespace obstacle_detector; + +ObstacleExtractor::ObstacleExtractor(ros::NodeHandle& nh, ros::NodeHandle& nh_local) : nh_(nh), nh_local_(nh_local) { + p_active_ = false; + + params_srv_ = nh_local_.advertiseService("params", &ObstacleExtractor::updateParams, this); + initialize(); +} + +ObstacleExtractor::~ObstacleExtractor() { + nh_local_.deleteParam("active"); + nh_local_.deleteParam("use_scan"); + nh_local_.deleteParam("use_pcl"); + + nh_local_.deleteParam("use_split_and_merge"); + nh_local_.deleteParam("circles_from_visibles"); + nh_local_.deleteParam("discard_converted_segments"); + nh_local_.deleteParam("transform_coordinates"); + + nh_local_.deleteParam("min_group_points"); + + nh_local_.deleteParam("max_group_distance"); + nh_local_.deleteParam("distance_proportion"); + nh_local_.deleteParam("max_split_distance"); + nh_local_.deleteParam("max_merge_separation"); + nh_local_.deleteParam("max_merge_spread"); + nh_local_.deleteParam("max_circle_radius"); + nh_local_.deleteParam("radius_enlargement"); + + nh_local_.deleteParam("min_x_limit"); + nh_local_.deleteParam("max_x_limit"); + nh_local_.deleteParam("min_y_limit"); + nh_local_.deleteParam("max_y_limit"); + + nh_local_.deleteParam("frame_id"); +} + +bool ObstacleExtractor::updateParams(std_srvs::Empty::Request &req, std_srvs::Empty::Response &res) { + bool prev_active = p_active_; + + nh_local_.param("active", p_active_, true); + nh_local_.param("use_scan", p_use_scan_, true); + nh_local_.param("use_pcl", p_use_pcl_, false); + + nh_local_.param("use_split_and_merge", p_use_split_and_merge_, true); + nh_local_.param("circles_from_visibles", p_circles_from_visibles_, true); + nh_local_.param("discard_converted_segments", p_discard_converted_segments_, true); + nh_local_.param("transform_coordinates", p_transform_coordinates_, true); + + nh_local_.param("min_group_points", p_min_group_points_, 5); + + nh_local_.param("max_group_distance", p_max_group_distance_, 0.1); + nh_local_.param("distance_proportion", p_distance_proportion_, 0.00628); + nh_local_.param("max_split_distance", p_max_split_distance_, 0.2); + nh_local_.param("max_merge_separation", p_max_merge_separation_, 0.2); + nh_local_.param("max_merge_spread", p_max_merge_spread_, 0.2); + nh_local_.param("max_circle_radius", p_max_circle_radius_, 0.6); + nh_local_.param("radius_enlargement", p_radius_enlargement_, 0.25); + + nh_local_.param("min_x_limit", p_min_x_limit_, -100.0); + nh_local_.param("max_x_limit", p_max_x_limit_, 100.0); + nh_local_.param("min_y_limit", p_min_y_limit_, -100.0); + nh_local_.param("max_y_limit", p_max_y_limit_, 100.0); + + nh_local_.param("frame_id", p_frame_id_, "map"); + nh_local_.param("base_frame_id", base_frame_id_, "automobile_lidar_lidar_link"); + + if (p_active_ != prev_active) { + if (p_active_) { + if (p_use_scan_){ + ROS_INFO_STREAM_ONCE("Received scan message"); + scan_sub_ = nh_.subscribe("automobile/scan", 10, &ObstacleExtractor::scanCallback, this); + } + else if (p_use_pcl_) + pcl_sub_ = nh_.subscribe("pcl", 10, &ObstacleExtractor::pclCallback, this); + + obstacles_pub_ = nh_.advertise("raw_obstacles", 10); + } + else { + // Send empty message + obstacle_detector::ObstaclesPtr obstacles_msg(new obstacle_detector::Obstacles); + obstacles_msg->header.frame_id = p_frame_id_; + obstacles_msg->header.stamp = ros::Time::now(); + obstacles_pub_.publish(obstacles_msg); + + scan_sub_.shutdown(); + pcl_sub_.shutdown(); + obstacles_pub_.shutdown(); + } + } + + return true; +} + +void ObstacleExtractor::scanCallback(const sensor_msgs::LaserScan::ConstPtr scan_msg) { + ROS_INFO_STREAM_ONCE("Received scan message"); + base_frame_id_ = scan_msg->header.frame_id; + stamp_ = scan_msg->header.stamp; + + double phi = scan_msg->angle_min; + + std::cout << "scan_msg->ranges: "; + for (const float r : scan_msg->ranges) { + std::cout << r << " "; + } + std::cout << std::endl; + + for (const float r : scan_msg->ranges) { + // if (r >= scan_msg->range_min && r <= scan_msg->range_max) + input_points_.push_back(Point::fromPoolarCoords(r, phi)); + phi += scan_msg->angle_increment; + } + + processPoints(); +} + +void ObstacleExtractor::pclCallback(const sensor_msgs::PointCloud::ConstPtr pcl_msg) { + base_frame_id_ = pcl_msg->header.frame_id; + stamp_ = pcl_msg->header.stamp; + + const auto range_channel = std::find_if(pcl_msg->channels.begin(), pcl_msg->channels.end(), [] (const sensor_msgs::ChannelFloat32& channel) { return channel.name == "range"; } ); + int i = 0; + for (const geometry_msgs::Point32& point : pcl_msg->points) { + auto point_copy = Point(point.x, point.y); + if (range_channel != pcl_msg->channels.end()) { + point_copy.range = range_channel->values.at(i++); + assert(point_copy.range >= 0.0); + ROS_INFO_STREAM_ONCE("Point cloud contains range information, an example value is " << point_copy.range); + } else { + ROS_WARN_ONCE("Point cloud does not contain range information, assuming point cloud origin aligns with lidar origin"); + } + input_points_.push_back(point_copy); + } + + processPoints(); +} + +void ObstacleExtractor::processPoints() { + segments_.clear(); + circles_.clear(); + + groupPoints(); // Grouping points simultaneously detects segments + mergeSegments(); + + detectCircles(); + mergeCircles(); + + publishObstacles(); + + input_points_.clear(); +} + +void ObstacleExtractor::groupPoints() { + static double sin_dp = sin(2.0 * p_distance_proportion_); + + PointSet point_set; + point_set.begin = input_points_.begin(); + point_set.end = input_points_.begin(); + point_set.num_points = 1; + point_set.is_visible = true; + + for (PointIterator point = input_points_.begin()++; point != input_points_.end(); ++point) { + double range = (*point).getRange(); + double distance = (*point - *point_set.end).length(); + + if (distance < p_max_group_distance_ + range * p_distance_proportion_) { + point_set.end = point; + point_set.num_points++; + } + else { + double prev_range = (*point_set.end).getRange(); + + // Heron's equation + double p = (range + prev_range + distance) / 2.0; + double S = sqrt(p * (p - range) * (p - prev_range) * (p - distance)); + double sin_d = 2.0 * S / (range * prev_range); // Sine of angle between beams + + // TODO: This condition can be fulfilled if the point are on the opposite sides + // of the scanner (angle = 180 deg). Needs another check. + if (abs(sin_d) < sin_dp && range < prev_range) + point_set.is_visible = false; + + detectSegments(point_set); + + // Begin new point set + point_set.begin = point; + point_set.end = point; + point_set.num_points = 1; + point_set.is_visible = (abs(sin_d) > sin_dp || range < prev_range); + } + } + + detectSegments(point_set); // Check the last point set too! +} + +void ObstacleExtractor::detectSegments(const PointSet& point_set) { + if (point_set.num_points < p_min_group_points_) + return; + + Segment segment(*point_set.begin, *point_set.end); // Use Iterative End Point Fit + + if (p_use_split_and_merge_) + segment = fitSegment(point_set); + + PointIterator set_divider; + double max_distance = 0.0; + double distance = 0.0; + + int split_index = 0; // Natural index of splitting point (counting from 1) + int point_index = 0; // Natural index of current point in the set + + // Seek the point of division + for (PointIterator point = point_set.begin; point != point_set.end; ++point) { + ++point_index; + + if ((distance = segment.distanceTo(*point)) >= max_distance) { + double r = (*point).getRange(); + + if (distance > p_max_split_distance_ + r * p_distance_proportion_) { + max_distance = distance; + set_divider = point; + split_index = point_index; + } + } + } + + // Split the set only if the sub-groups are not 'small' + if (max_distance > 0.0 && split_index > p_min_group_points_ && split_index < point_set.num_points - p_min_group_points_) { + set_divider = input_points_.insert(set_divider, *set_divider); // Clone the dividing point for each group + + PointSet subset1, subset2; + subset1.begin = point_set.begin; + subset1.end = set_divider; + subset1.num_points = split_index; + subset1.is_visible = point_set.is_visible; + + subset2.begin = ++set_divider; + subset2.end = point_set.end; + subset2.num_points = point_set.num_points - split_index; + subset2.is_visible = point_set.is_visible; + + detectSegments(subset1); + detectSegments(subset2); + } else { // Add the segment + if (!p_use_split_and_merge_) + segment = fitSegment(point_set); + + segments_.push_back(segment); + } +} + +void ObstacleExtractor::mergeSegments() { + for (auto i = segments_.begin(); i != segments_.end(); ++i) { + for (auto j = i; j != segments_.end(); ++j) { + Segment merged_segment; + + if (compareSegments(*i, *j, merged_segment)) { + auto temp_itr = segments_.insert(i, merged_segment); + segments_.erase(i); + segments_.erase(j); + i = --temp_itr; // Check the new segment against others + break; + } + } + } +} + +bool ObstacleExtractor::compareSegments(const Segment& s1, const Segment& s2, Segment& merged_segment) { + if (&s1 == &s2) + return false; + + // Segments must be provided counter-clockwise + if (s1.first_point.cross(s2.first_point) < 0.0) + return compareSegments(s2, s1, merged_segment); + + if (checkSegmentsProximity(s1, s2)) { + vector point_sets; + point_sets.insert(point_sets.end(), s1.point_sets.begin(), s1.point_sets.end()); + point_sets.insert(point_sets.end(), s2.point_sets.begin(), s2.point_sets.end()); + + Segment segment = fitSegment(point_sets); + + if (checkSegmentsCollinearity(segment, s1, s2)) { + merged_segment = segment; + return true; + } + } + + return false; +} + +bool ObstacleExtractor::checkSegmentsProximity(const Segment& s1, const Segment& s2) { + return (s1.trueDistanceTo(s2.first_point) < p_max_merge_separation_ || + s1.trueDistanceTo(s2.last_point) < p_max_merge_separation_ || + s2.trueDistanceTo(s1.first_point) < p_max_merge_separation_ || + s2.trueDistanceTo(s1.last_point) < p_max_merge_separation_); +} + +bool ObstacleExtractor::checkSegmentsCollinearity(const Segment& segment, const Segment& s1, const Segment& s2) { + return (segment.distanceTo(s1.first_point) < p_max_merge_spread_ && + segment.distanceTo(s1.last_point) < p_max_merge_spread_ && + segment.distanceTo(s2.first_point) < p_max_merge_spread_ && + segment.distanceTo(s2.last_point) < p_max_merge_spread_); +} + +void ObstacleExtractor::detectCircles() { + for (auto segment = segments_.begin(); segment != segments_.end(); ++segment) { + if (p_circles_from_visibles_) { + bool segment_is_visible = true; + for (const PointSet& ps : segment->point_sets) { + if (!ps.is_visible) { + segment_is_visible = false; + break; + } + } + if (!segment_is_visible) + continue; + } + + Circle circle(*segment); + circle.radius += p_radius_enlargement_; + + if (circle.radius < p_max_circle_radius_) { + circles_.push_back(circle); + + if (p_discard_converted_segments_) { + segment = segments_.erase(segment); + --segment; + } + } + } +} + +void ObstacleExtractor::mergeCircles() { + for (auto i = circles_.begin(); i != circles_.end(); ++i) { + for (auto j = i; j != circles_.end(); ++j) { + Circle merged_circle; + + if (compareCircles(*i, *j, merged_circle)) { + auto temp_itr = circles_.insert(i, merged_circle); + circles_.erase(i); + circles_.erase(j); + i = --temp_itr; + break; + } + } + } +} + +bool ObstacleExtractor::compareCircles(const Circle& c1, const Circle& c2, Circle& merged_circle) { + if (&c1 == &c2) + return false; + + // If circle c1 is fully inside c2 - merge and leave as c2 + if (c2.radius - c1.radius >= (c2.center - c1.center).length()) { + merged_circle = c2; + return true; + } + + // If circle c2 is fully inside c1 - merge and leave as c1 + if (c1.radius - c2.radius >= (c2.center - c1.center).length()) { + merged_circle = c1; + return true; + } + + // If circles intersect and are 'small' - merge + if (c1.radius + c2.radius >= (c2.center - c1.center).length()) { + Point center = c1.center + (c2.center - c1.center) * c1.radius / (c1.radius + c2.radius); + double radius = (c1.center - center).length() + c1.radius; + + Circle circle(center, radius); + circle.radius += max(c1.radius, c2.radius); + + if (circle.radius < p_max_circle_radius_) { + circle.point_sets.insert(circle.point_sets.end(), c1.point_sets.begin(), c1.point_sets.end()); + circle.point_sets.insert(circle.point_sets.end(), c2.point_sets.begin(), c2.point_sets.end()); + merged_circle = circle; + return true; + } + } + + return false; +} + +void ObstacleExtractor::publishObstacles() { + obstacle_detector::ObstaclesPtr obstacles_msg(new obstacle_detector::Obstacles); + obstacles_msg->header.stamp = stamp_; + + if (p_transform_coordinates_) { + tf::StampedTransform transform; + + try { + tf_listener_.waitForTransform(p_frame_id_, base_frame_id_, stamp_, ros::Duration(0.1)); + tf_listener_.lookupTransform(p_frame_id_, base_frame_id_, stamp_, transform); + ROS_INFO_STREAM_ONCE("Transform found"); + } + catch (tf::TransformException& ex) { + ROS_INFO_STREAM(ex.what()); + return; + } + + for (Segment& s : segments_) { + s.first_point = transformPoint(s.first_point, transform); + s.last_point = transformPoint(s.last_point, transform); + } + + for (Circle& c : circles_) + c.center = transformPoint(c.center, transform); + + obstacles_msg->header.frame_id = p_frame_id_; + } + else + obstacles_msg->header.frame_id = base_frame_id_; + + + for (const Segment& s : segments_) { + SegmentObstacle segment; + + segment.first_point.x = s.first_point.x; + segment.first_point.y = s.first_point.y; + segment.last_point.x = s.last_point.x; + segment.last_point.y = s.last_point.y; + + obstacles_msg->segments.push_back(segment); + } + + for (const Circle& c : circles_) { + if (c.center.x > p_min_x_limit_ && c.center.x < p_max_x_limit_ && + c.center.y > p_min_y_limit_ && c.center.y < p_max_y_limit_) { + CircleObstacle circle; + + circle.center.x = c.center.x; + circle.center.y = c.center.y; + circle.velocity.x = 0.0; + circle.velocity.y = 0.0; + circle.radius = c.radius; + circle.true_radius = c.radius - p_radius_enlargement_; + + obstacles_msg->circles.push_back(circle); + } + } + + obstacles_pub_.publish(obstacles_msg); +} diff --git a/src/obstacle_detector/src/obstacle_publisher.cpp b/src/obstacle_detector/src/obstacle_publisher.cpp new file mode 100644 index 0000000..2ea1475 --- /dev/null +++ b/src/obstacle_detector/src/obstacle_publisher.cpp @@ -0,0 +1,233 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2017, Poznan University of Technology + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Poznan University of Technology nor the names + * of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Author: Mateusz Przybyla + */ + +#include "obstacle_detector/obstacle_publisher.h" + +using namespace std; +using namespace obstacle_detector; + +ObstaclePublisher::ObstaclePublisher(ros::NodeHandle& nh, ros::NodeHandle& nh_local) : nh_(nh), nh_local_(nh_local) { + p_active_ = false; + t_ = 0.0; + + timer_ = nh_.createTimer(ros::Duration(1.0), &ObstaclePublisher::timerCallback, this, false, false); + params_srv_ = nh_local_.advertiseService("params", &ObstaclePublisher::updateParams, this); + initialize(); +} + +ObstaclePublisher::~ObstaclePublisher() { + nh_local_.deleteParam("active"); + nh_local_.deleteParam("reset"); + + nh_local_.deleteParam("fusion_example"); + nh_local_.deleteParam("fission_example"); + + nh_local_.deleteParam("loop_rate"); + nh_local_.deleteParam("radius_margin"); + + nh_local_.deleteParam("x_vector"); + nh_local_.deleteParam("y_vector"); + nh_local_.deleteParam("r_vector"); + + nh_local_.deleteParam("vx_vector"); + nh_local_.deleteParam("vy_vector"); + + nh_local_.deleteParam("frame_id"); +} + +bool ObstaclePublisher::updateParams(std_srvs::Empty::Request& req, std_srvs::Empty::Response& res) { + bool prev_active = p_active_; + + nh_local_.param("active", p_active_, true); + nh_local_.param("reset", p_reset_, false); + + nh_local_.param("fusion_example", p_fusion_example_, false); + nh_local_.param("fission_example", p_fission_example_, false); + + nh_local_.param("loop_rate", p_loop_rate_, 10.0); + nh_local_.param("radius_margin", p_radius_margin_, 0.25); + + nh_local_.getParam("x_vector", p_x_vector_); + nh_local_.getParam("y_vector", p_y_vector_); + nh_local_.getParam("r_vector", p_r_vector_); + + nh_local_.getParam("vx_vector", p_vx_vector_); + nh_local_.getParam("vy_vector", p_vy_vector_); + + nh_local_.param("frame_id", p_frame_id_, string("map")); + + p_sampling_time_ = 1.0 / p_loop_rate_; + timer_.setPeriod(ros::Duration(p_sampling_time_), false); + + if (p_active_ != prev_active) { + if (p_active_) { + obstacle_pub_ = nh_.advertise("obstacles", 10); + timer_.start(); + } + else { + obstacle_pub_.shutdown(); + timer_.stop(); + } + } + + obstacles_.header.frame_id = p_frame_id_; + obstacles_.circles.clear(); + + if (p_x_vector_.size() != p_y_vector_.size() || p_x_vector_.size() != p_r_vector_.size() || + p_x_vector_.size() != p_vx_vector_.size() || p_x_vector_.size() != p_vy_vector_.size()) + return false; + + for (int idx = 0; idx < p_x_vector_.size(); ++idx) { + CircleObstacle circle; + circle.center.x = p_x_vector_[idx]; + circle.center.y = p_y_vector_[idx]; + circle.radius = p_r_vector_[idx]; + circle.true_radius = p_r_vector_[idx] - p_radius_margin_;; + + circle.velocity.x = p_vx_vector_[idx]; + circle.velocity.y = p_vy_vector_[idx]; + + obstacles_.circles.push_back(circle); + } + + if (p_reset_) + reset(); + + return true; +} + +void ObstaclePublisher::timerCallback(const ros::TimerEvent& e) { + t_ += p_sampling_time_; + + calculateObstaclesPositions(p_sampling_time_); + + if (p_fusion_example_) + fusionExample(t_); + else if (p_fission_example_) + fissionExample(t_); + + if (obstacles_.circles.size() > 0) + publishObstacles(); +} + +void ObstaclePublisher::calculateObstaclesPositions(double dt) { + for (auto& circ : obstacles_.circles) { + circ.center.x += circ.velocity.x * dt; + circ.center.y += circ.velocity.y * dt; + } +} + +void ObstaclePublisher::fusionExample(double t) { + CircleObstacle circ1, circ2; + + obstacles_.circles.clear(); + + if (t < 5.0) { + circ1.center.x = -1.20 + 0.2 * t; + circ1.center.y = 0.0; + circ1.radius = 0.20; + + circ2.center.x = 1.20 - 0.2 * t; + circ2.center.y = 0.0; + circ2.radius = 0.20; + + obstacles_.circles.push_back(circ1); + obstacles_.circles.push_back(circ2); + } + else if (t < 15.0) { + circ1.center.x = 0.0; + circ1.center.y = 0.0; + circ1.radius = 0.20 + 0.20 * exp(-(t - 5.0) / 1.0); + + obstacles_.circles.push_back(circ1); + } + else if (t > 20.0) + reset(); + + circ1.true_radius = circ1.radius; + circ2.true_radius = circ2.radius; +} + +void ObstaclePublisher::fissionExample(double t) { + CircleObstacle circ1, circ2; + + obstacles_.circles.clear(); + + if (t < 5.0) { + circ1.center.x = 0.0; + circ1.center.y = 0.0; + circ1.radius = 0.20; + + obstacles_.circles.push_back(circ1); + } + else if (t < 6.0) { + circ1.center.x = 0.0; + circ1.center.y = 0.0; + circ1.radius = 0.20 + 0.20 * (1.0 - exp(-(t - 5.0) / 1.0)); + + obstacles_.circles.push_back(circ1); + } + else if (t < 16.0){ + circ1.center.x = -0.20 - 0.2 * (t - 6.0); + circ1.center.y = 0.0; + circ1.radius = 0.20; + + circ2.center.x = 0.20 + 0.2 * (t - 6.0); + circ2.center.y = 0.0; + circ2.radius = 0.20; + + obstacles_.circles.push_back(circ1); + obstacles_.circles.push_back(circ2); + } + else if (t > 20.0) + reset(); + + circ1.true_radius = circ1.radius; + circ2.true_radius = circ2.radius; +} + +void ObstaclePublisher::publishObstacles() { + obstacle_detector::ObstaclesPtr obstacles_msg(new obstacle_detector::Obstacles); + *obstacles_msg = obstacles_; + + obstacles_msg->header.stamp = ros::Time::now(); + obstacle_pub_.publish(obstacles_msg); +} + +void ObstaclePublisher::reset() { + t_ = 0.0; + p_reset_ = false; + nh_local_.setParam("reset", false); +} diff --git a/src/obstacle_detector/src/obstacle_tracker.cpp b/src/obstacle_detector/src/obstacle_tracker.cpp new file mode 100644 index 0000000..5c04d52 --- /dev/null +++ b/src/obstacle_detector/src/obstacle_tracker.cpp @@ -0,0 +1,735 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2017, Poznan University of Technology + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Poznan University of Technology nor the names + * of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Author: Mateusz Przybyla + */ + +#include "obstacle_detector/obstacle_tracker.h" + +using namespace obstacle_detector; +using namespace arma; +using namespace std; + +ObstacleTracker::ObstacleTracker(ros::NodeHandle& nh, ros::NodeHandle& nh_local) : nh_(nh), nh_local_(nh_local) { + p_active_ = false; + + timer_ = nh_.createTimer(ros::Duration(1.0), &ObstacleTracker::timerCallback, this, false, false); + params_srv_ = nh_local_.advertiseService("params", &ObstacleTracker::updateParams, this); + + initialize(); +} + +ObstacleTracker::~ObstacleTracker() { + nh_local_.deleteParam("active"); + nh_local_.deleteParam("copy_segments"); + nh_local_.deleteParam("compensate_robot_velocity"); + + nh_local_.deleteParam("loop_rate"); + nh_local_.deleteParam("tracking_duration"); + nh_local_.deleteParam("min_correspondence_cost"); + nh_local_.deleteParam("std_correspondence_dev"); + nh_local_.deleteParam("process_variance"); + nh_local_.deleteParam("process_rate_variance"); + nh_local_.deleteParam("measurement_variance"); + + nh_local_.deleteParam("frame_id"); +} + +bool ObstacleTracker::updateParams(std_srvs::Empty::Request &req, std_srvs::Empty::Response &res) { + bool prev_active = p_active_; + + nh_local_.param("active", p_active_, true); + nh_local_.param("copy_segments", p_copy_segments_, true); + nh_local_.param("compensate_robot_velocity", p_compensate_robot_velocity_, false); // Before using this, consider using pointcloud input from map frame instead of base_link frame + nh_local_.param("sensor_rate", p_sensor_rate_, 10.0); + nh_local_.param("loop_rate", p_loop_rate_, 100.0); + p_sampling_time_ = 1.0 / p_loop_rate_; + + nh_local_.param("tracking_duration", p_tracking_duration_, 2.0); + nh_local_.param("min_correspondence_cost", p_min_correspondence_cost_, 0.3); + nh_local_.param("std_correspondence_dev", p_std_correspondence_dev_, 0.15); + nh_local_.param("process_variance", p_process_variance_, 0.01); + nh_local_.param("process_rate_variance", p_process_rate_variance_, 0.1); + nh_local_.param("measurement_variance", p_measurement_variance_, 1.0); + + nh_local_.param("frame_id", p_frame_id_, string("map")); + obstacles_.header.frame_id = p_frame_id_; + + TrackedCircleObstacle::setSamplingTime(p_sampling_time_); + TrackedCircleObstacle::setCounterSize(static_cast(p_loop_rate_ * p_tracking_duration_)); + TrackedCircleObstacle::setCovariances(p_process_variance_, p_process_rate_variance_, p_measurement_variance_); + TrackedSegmentObstacle::setSamplingTime(p_sampling_time_); + TrackedSegmentObstacle::setCounterSize(static_cast(p_loop_rate_ * p_tracking_duration_)); + TrackedSegmentObstacle::setCovariances(p_process_variance_, p_process_rate_variance_, p_measurement_variance_); + + timer_.setPeriod(ros::Duration(p_sampling_time_), false); + + if (p_active_ != prev_active) { + if (p_active_) { + if(p_compensate_robot_velocity_) + odom_sub_ = nh_.subscribe("/odom", 1, &ObstacleTracker::odomCallback, this); + obstacles_sub_ = nh_.subscribe("raw_obstacles", 10, &ObstacleTracker::obstaclesCallback, this); + obstacles_pub_ = nh_.advertise("tracked_obstacles", 10); + timer_.start(); + } + else { + // Send empty message + obstacle_detector::ObstaclesPtr obstacles_msg(new obstacle_detector::Obstacles); + obstacles_msg->header.frame_id = obstacles_.header.frame_id; + obstacles_msg->header.stamp = ros::Time::now(); + obstacles_pub_.publish(obstacles_msg); + + obstacles_sub_.shutdown(); + obstacles_pub_.shutdown(); + + tracked_circle_obstacles_.clear(); + untracked_circle_obstacles_.clear(); + tracked_segment_obstacles_.clear(); + untracked_segment_obstacles_.clear(); + + timer_.stop(); + } + } + + return true; +} + +void ObstacleTracker::timerCallback(const ros::TimerEvent&) { + updateObstacles(); + publishObstacles(); +} + +void ObstacleTracker::odomCallback(const nav_msgs::Odometry::ConstPtr &msg) +{ + odom_ = *msg; +} + +void ObstacleTracker::obstaclesCallback(const obstacle_detector::Obstacles::ConstPtr new_obstacles) { + obstaclesCallbackCircles(new_obstacles); + obstaclesCallbackSegments(new_obstacles); +} + +void ObstacleTracker::obstaclesCallbackCircles(const obstacle_detector::Obstacles::ConstPtr new_obstacles) { + if (new_obstacles->circles.size() > 0) + radius_margin_ = new_obstacles->circles[0].radius - new_obstacles->circles[0].true_radius; + + int N = new_obstacles->circles.size(); + int T = tracked_circle_obstacles_.size(); + int U = untracked_circle_obstacles_.size(); + + if (T + U == 0) { + untracked_circle_obstacles_.assign(new_obstacles->circles.begin(), new_obstacles->circles.end()); + return; + } + + mat cost_matrix; + calculateCostMatrix(new_obstacles->circles, cost_matrix); + + vector row_min_indices; + calculateRowMinIndices(cost_matrix, row_min_indices, T, U); + + vector col_min_indices; + calculateColMinIndices(cost_matrix, col_min_indices, T, U); + + vector used_old_obstacles; + vector used_new_obstacles; + + vector new_tracked_obstacles; + vector new_untracked_obstacles; + + // Check for fusion (only tracked obstacles) + for (int i = 0; i < T-1; ++i) { + if (fusionObstacleUsed(i, col_min_indices, used_new_obstacles, used_old_obstacles)) + continue; + + vector fusion_indices; + fusion_indices.push_back(i); + + for (int j = i+1; j < T; ++j) { + if (fusionObstaclesCorrespond(i, j, col_min_indices, used_old_obstacles)) + fusion_indices.push_back(j); + } + + if (fusion_indices.size() > 1) { + fuseObstacles(fusion_indices, col_min_indices, new_tracked_obstacles, new_obstacles); + + // Mark used old and new obstacles + used_old_obstacles.insert(used_old_obstacles.end(), fusion_indices.begin(), fusion_indices.end()); + used_new_obstacles.push_back(col_min_indices[i]); + } + } + + // Check for fission (only tracked obstacles) + for (int i = 0; i < N-1; ++i) { + if (fissionObstacleUsed(i, T, row_min_indices, used_new_obstacles, used_old_obstacles)) + continue; + + vector fission_indices; + fission_indices.push_back(i); + + for (int j = i+1; j < N; ++j) { + if (fissionObstaclesCorrespond(i, j, row_min_indices, used_new_obstacles)) + fission_indices.push_back(j); + } + + if (fission_indices.size() > 1) { + fissureObstacle(fission_indices, row_min_indices, new_tracked_obstacles, new_obstacles); + + // Mark used old and new obstacles + used_old_obstacles.push_back(row_min_indices[i]); + used_new_obstacles.insert(used_new_obstacles.end(), fission_indices.begin(), fission_indices.end()); + } + } + + // Check for other possibilities + for (int n = 0; n < N; ++n) { + if (find(used_new_obstacles.begin(), used_new_obstacles.end(), n) != used_new_obstacles.end()) + continue; + + if (row_min_indices[n] == -1) { + new_untracked_obstacles.push_back(new_obstacles->circles[n]); + } + else if (find(used_old_obstacles.begin(), used_old_obstacles.end(), row_min_indices[n]) == used_old_obstacles.end()) { + if (row_min_indices[n] >= 0 && row_min_indices[n] < T) { + tracked_circle_obstacles_[row_min_indices[n]].correctState(new_obstacles->circles[n]); + } + else if (row_min_indices[n] >= T) { + TrackedCircleObstacle to(untracked_circle_obstacles_[row_min_indices[n] - T]); + to.correctState(new_obstacles->circles[n]); + for (int i = 0; i < static_cast(p_loop_rate_ / p_sensor_rate_); ++i) + to.updateState(); + + new_tracked_obstacles.push_back(to); + } + + used_new_obstacles.push_back(n); + } + } + + // Remove tracked obstacles that are no longer existent due to fusion or fission and insert new ones + // Sort in descending order to remove from back of the list + sort(used_old_obstacles.rbegin(), used_old_obstacles.rend()); + for (int idx : used_old_obstacles) + tracked_circle_obstacles_.erase(tracked_circle_obstacles_.begin() + idx); + + tracked_circle_obstacles_.insert(tracked_circle_obstacles_.end(), new_tracked_obstacles.begin(), new_tracked_obstacles.end()); + + // Remove old untracked obstacles and save new ones + untracked_circle_obstacles_.clear(); + untracked_circle_obstacles_.assign(new_untracked_obstacles.begin(), new_untracked_obstacles.end()); +} + +void ObstacleTracker::obstaclesCallbackSegments(const obstacle_detector::Obstacles::ConstPtr new_obstacles) { + int N = new_obstacles->segments.size(); + int T = tracked_segment_obstacles_.size(); + int U = untracked_segment_obstacles_.size(); + + if (T + U == 0) { + untracked_segment_obstacles_.assign(new_obstacles->segments.begin(), new_obstacles->segments.end()); + return; + } + + mat cost_matrix; + calculateCostMatrix(new_obstacles->segments, cost_matrix); + + vector row_min_indices; + calculateRowMinIndices(cost_matrix, row_min_indices, T, U); + + vector col_min_indices; + calculateColMinIndices(cost_matrix, col_min_indices, T, U); + + vector used_old_obstacles; + vector used_new_obstacles; + + vector new_tracked_obstacles; + vector new_untracked_obstacles; + + // Check for fusion (only tracked obstacles) + for (int i = 0; i < T-1; ++i) { + if (fusionObstacleUsed(i, col_min_indices, used_new_obstacles, used_old_obstacles)) + continue; + + vector fusion_indices; + fusion_indices.push_back(i); + + for (int j = i+1; j < T; ++j) { + if (fusionObstaclesCorrespond(i, j, col_min_indices, used_old_obstacles)) + fusion_indices.push_back(j); + } + + if (fusion_indices.size() > 1) { + fuseObstacles(fusion_indices, col_min_indices, new_tracked_obstacles, new_obstacles); + + // Mark used old and new obstacles + used_old_obstacles.insert(used_old_obstacles.end(), fusion_indices.begin(), fusion_indices.end()); + used_new_obstacles.push_back(col_min_indices[i]); + } + } + + // Check for fission (only tracked obstacles) + for (int i = 0; i < N-1; ++i) { + if (fissionObstacleUsed(i, T, row_min_indices, used_new_obstacles, used_old_obstacles)) + continue; + + vector fission_indices; + fission_indices.push_back(i); + + for (int j = i+1; j < N; ++j) { + if (fissionObstaclesCorrespond(i, j, row_min_indices, used_new_obstacles)) + fission_indices.push_back(j); + } + + if (fission_indices.size() > 1) { + fissureObstacle(fission_indices, row_min_indices, new_tracked_obstacles, new_obstacles); + + // Mark used old and new obstacles + used_old_obstacles.push_back(row_min_indices[i]); + used_new_obstacles.insert(used_new_obstacles.end(), fission_indices.begin(), fission_indices.end()); + } + } + + // Check for other possibilities + for (int n = 0; n < N; ++n) { + if (find(used_new_obstacles.begin(), used_new_obstacles.end(), n) != used_new_obstacles.end()) + continue; + + if (row_min_indices[n] == -1) { + new_untracked_obstacles.push_back(new_obstacles->segments[n]); + } + else if (find(used_old_obstacles.begin(), used_old_obstacles.end(), row_min_indices[n]) == used_old_obstacles.end()) { + if (row_min_indices[n] >= 0 && row_min_indices[n] < T) { + tracked_segment_obstacles_[row_min_indices[n]].correctState(new_obstacles->segments[n]); + } + else if (row_min_indices[n] >= T) { + TrackedSegmentObstacle to(untracked_segment_obstacles_[row_min_indices[n] - T]); + to.correctState(new_obstacles->segments[n]); + for (int i = 0; i < static_cast(p_loop_rate_ / p_sensor_rate_); ++i) + to.updateState(); + + new_tracked_obstacles.push_back(to); + } + + used_new_obstacles.push_back(n); + } + } + + // Remove tracked obstacles that are no longer existent due to fusion or fission and insert new ones + // Sort in descending order to remove from back of the list + sort(used_old_obstacles.rbegin(), used_old_obstacles.rend()); + for (int idx : used_old_obstacles) + tracked_segment_obstacles_.erase(tracked_segment_obstacles_.begin() + idx); + + tracked_segment_obstacles_.insert(tracked_segment_obstacles_.end(), new_tracked_obstacles.begin(), new_tracked_obstacles.end()); + + // Remove old untracked obstacles and save new ones + untracked_segment_obstacles_.clear(); + untracked_segment_obstacles_.assign(new_untracked_obstacles.begin(), new_untracked_obstacles.end()); +} + +double ObstacleTracker::obstacleCostFunction(const CircleObstacle& new_obstacle, const CircleObstacle& old_obstacle) { + mat distribution = mat(2, 2).zeros(); + vec relative_position = vec(2).zeros(); + + double cost = 0.0; + double penalty = 1.0; + double tp = 1.0 / p_sensor_rate_; + + double direction = atan2(old_obstacle.velocity.y, old_obstacle.velocity.x); + + geometry_msgs::Point new_center = transformPoint(new_obstacle.center, 0.0, 0.0, -direction); + geometry_msgs::Point old_center = transformPoint(old_obstacle.center, 0.0, 0.0, -direction); + + distribution(0, 0) = pow(p_std_correspondence_dev_, 2.0) + squaredLength(old_obstacle.velocity) * pow(tp, 2.0); + distribution(1, 1) = pow(p_std_correspondence_dev_, 2.0); + + relative_position(0) = new_center.x - old_center.x - tp * length(old_obstacle.velocity); + relative_position(1) = new_center.y - old_center.y; + + cost = sqrt(pow(new_obstacle.center.x - old_obstacle.center.x, 2.0) + pow(new_obstacle.center.y - old_obstacle.center.y, 2.0) + pow(new_obstacle.radius - old_obstacle.radius, 2.0)); + + mat a = -0.5 * trans(relative_position) * distribution * relative_position; + penalty = exp(a(0, 0)); + + // TODO: Check values for cost/penalty in common situations + // return cost / penalty; + return cost / 1.0; +} + +double ObstacleTracker::obstacleCostFunction(const SegmentObstacle& new_obstacle, const SegmentObstacle& old_obstacle) { + return sqrt(pow(new_obstacle.first_point.x - old_obstacle.first_point.x, 2.0) + pow(new_obstacle.first_point.y - old_obstacle.first_point.y, 2.0) + pow(new_obstacle.last_point.x - old_obstacle.last_point.x, 2.0) + pow(new_obstacle.last_point.y - old_obstacle.last_point.y, 2.0)); +} + +void ObstacleTracker::calculateCostMatrix(const vector& new_obstacles, mat& cost_matrix) { + /* + * Cost between two obstacles represents their difference. + * The bigger the cost, the less similar they are. + * N rows of cost_matrix represent new obstacles. + * T+U columns of cost matrix represent old tracked and untracked obstacles. + */ + int N = new_obstacles.size(); + int T = tracked_circle_obstacles_.size(); + int U = untracked_circle_obstacles_.size(); + + cost_matrix = mat(N, T + U, fill::zeros); + + for (int n = 0; n < N; ++n) { + for (int t = 0; t < T; ++t) + cost_matrix(n, t) = obstacleCostFunction(new_obstacles[n], tracked_circle_obstacles_[t].getObstacle()); + + for (int u = 0; u < U; ++u) + cost_matrix(n, u + T) = obstacleCostFunction(new_obstacles[n], untracked_circle_obstacles_[u]); + } +} + +void ObstacleTracker::calculateCostMatrix(const vector& new_obstacles, mat& cost_matrix) { + /* + * Cost between two obstacles represents their difference. + * The bigger the cost, the less similar they are. + * N rows of cost_matrix represent new obstacles. + * T+U columns of cost matrix represent old tracked and untracked obstacles. + */ + int N = new_obstacles.size(); + int T = tracked_segment_obstacles_.size(); + int U = untracked_segment_obstacles_.size(); + + cost_matrix = mat(N, T + U, fill::zeros); + + for (int n = 0; n < N; ++n) { + for (int t = 0; t < T; ++t) + cost_matrix(n, t) = obstacleCostFunction(new_obstacles[n], tracked_segment_obstacles_[t].getObstacle()); + + for (int u = 0; u < U; ++u) + cost_matrix(n, u + T) = obstacleCostFunction(new_obstacles[n], untracked_segment_obstacles_[u]); + } +} + +void ObstacleTracker::calculateRowMinIndices(const mat& cost_matrix, vector& row_min_indices, const int T, const int U) { + /* + * Vector of row minimal indices keeps the indices of old obstacles (tracked and untracked) + * that have the minimum cost related to each of new obstacles, i.e. row_min_indices[n] + * keeps the index of old obstacle that has the minimum cost with n-th new obstacle. + */ + int N = cost_matrix.n_rows; + + row_min_indices.assign(N, -1); // Minimum index -1 means no correspondence has been found + + for (int n = 0; n < N; ++n) { + double min_cost = p_min_correspondence_cost_; + + for (int t = 0; t < T; ++t) { + if (cost_matrix(n, t) < min_cost) { + min_cost = cost_matrix(n, t); + row_min_indices[n] = t; + } + } + + for (int u = 0; u < U; ++u) { + if (cost_matrix(n, u + T) < min_cost) { + min_cost = cost_matrix(n, u + T); + row_min_indices[n] = u + T; + } + } + } +} + +void ObstacleTracker::calculateColMinIndices(const mat& cost_matrix, vector& col_min_indices, const int T, const int U) { + /* + * Vector of column minimal indices keeps the indices of new obstacles that has the minimum + * cost related to each of old (tracked and untracked) obstacles, i.e. col_min_indices[i] + * keeps the index of new obstacle that has the minimum cost with i-th old obstacle. + */ + int N = cost_matrix.n_rows; + + col_min_indices.assign(T + U, -1); // Minimum index -1 means no correspondence has been found + + for (int t = 0; t < T; ++t) { + double min_cost = p_min_correspondence_cost_; + + for (int n = 0; n < N; ++n) { + if (cost_matrix(n, t) < min_cost) { + min_cost = cost_matrix(n, t); + col_min_indices[t] = n; + } + } + } + + for (int u = 0; u < U; ++u) { + double min_cost = p_min_correspondence_cost_; + + for (int n = 0; n < N; ++n) { + if (cost_matrix(n, u + T) < min_cost) { + min_cost = cost_matrix(n, u + T); + col_min_indices[u + T] = n; + } + } + } +} + +bool ObstacleTracker::fusionObstacleUsed(const int idx, const vector &col_min_indices, const vector &used_new, const vector &used_old) { + /* + * This function returns true if: + * - idx-th old obstacle was already used + * - obstacle to which idx-th old obstacle corresponds was already used + * - there is no corresponding obstacle + */ + + return (find(used_old.begin(), used_old.end(), idx) != used_old.end() || + find(used_new.begin(), used_new.end(), col_min_indices[idx]) != used_new.end() || + col_min_indices[idx] < 0); +} + +bool ObstacleTracker::fusionObstaclesCorrespond(const int idx, const int jdx, const vector& col_min_indices, const vector& used_old) { + /* + * This function returns true if: + * - both old obstacles correspond to the same new obstacle + * - jdx-th old obstacle was not yet used + */ + + return (col_min_indices[idx] == col_min_indices[jdx] && + find(used_old.begin(), used_old.end(), jdx) == used_old.end()); +} + +bool ObstacleTracker::fissionObstacleUsed(const int idx, const int T, const vector& row_min_indices, const vector& used_new, const vector& used_old) { + /* + * This function returns true if: + * - idx-th new obstacle was already used + * - obstacle to which idx-th new obstacle corresponds was already used + * - there is no corresponding obstacle + * - obstacle to which idx-th new obstacle corresponds is untracked + */ + + return (find(used_new.begin(), used_new.end(), idx) != used_new.end() || + find(used_old.begin(), used_old.end(), row_min_indices[idx]) != used_old.end() || + row_min_indices[idx] < 0 || + row_min_indices[idx] >= T); +} + +bool ObstacleTracker::fissionObstaclesCorrespond(const int idx, const int jdx, const vector& row_min_indices, const vector& used_new) { + /* + * This function returns true if: + * - both new obstacles correspond to the same old obstacle + * - jdx-th new obstacle was not yet used + */ + + return (row_min_indices[idx] == row_min_indices[jdx] && + find(used_new.begin(), used_new.end(), jdx) == used_new.end()); +} + +void ObstacleTracker::fuseObstacles(const vector& fusion_indices, const vector &col_min_indices, + vector& new_tracked, const Obstacles::ConstPtr& new_obstacles) { + CircleObstacle c; + + double sum_var_x = 0.0; + double sum_var_y = 0.0; + double sum_var_vx = 0.0; + double sum_var_vy = 0.0; + double sum_var_r = 0.0; + + for (int idx : fusion_indices) { + c.center.x += tracked_circle_obstacles_[idx].getObstacle().center.x / tracked_circle_obstacles_[idx].getKFx().P(0,0); + c.center.y += tracked_circle_obstacles_[idx].getObstacle().center.y / tracked_circle_obstacles_[idx].getKFy().P(0,0); + c.velocity.x += tracked_circle_obstacles_[idx].getObstacle().velocity.x / tracked_circle_obstacles_[idx].getKFx().P(1,1); + c.velocity.y += tracked_circle_obstacles_[idx].getObstacle().velocity.y / tracked_circle_obstacles_[idx].getKFy().P(1,1); + c.radius += tracked_circle_obstacles_[idx].getObstacle().radius / tracked_circle_obstacles_[idx].getKFr().P(0,0); + + sum_var_x += 1.0 / tracked_circle_obstacles_[idx].getKFx().P(0,0); + sum_var_y += 1.0 / tracked_circle_obstacles_[idx].getKFy().P(0,0); + sum_var_vx += 1.0 / tracked_circle_obstacles_[idx].getKFx().P(1,1); + sum_var_vy += 1.0 / tracked_circle_obstacles_[idx].getKFy().P(1,1); + sum_var_r += 1.0 / tracked_circle_obstacles_[idx].getKFr().P(0,0); + } + + c.center.x /= sum_var_x; + c.center.y /= sum_var_y; + c.velocity.x /= sum_var_vx; + c.velocity.y /= sum_var_vy; + c.radius /= sum_var_r; + + TrackedCircleObstacle to(c); + to.correctState(new_obstacles->circles[col_min_indices[fusion_indices.front()]]); + for (int i = 0; i < static_cast(p_loop_rate_ / p_sensor_rate_); ++i) + to.updateState(); + + new_tracked.push_back(to); +} + +void ObstacleTracker::fuseObstacles(const vector& fusion_indices, const vector &col_min_indices, + vector& new_tracked, const Obstacles::ConstPtr& new_obstacles) { + SegmentObstacle c; + + double sum_var_x1 = 0.0; + double sum_var_y1 = 0.0; + double sum_var_x2 = 0.0; + double sum_var_y2 = 0.0; + double sum_var_vx1 = 0.0; + double sum_var_vy1 = 0.0; + double sum_var_vx2 = 0.0; + double sum_var_vy2 = 0.0; + + for (int idx : fusion_indices) { + c.first_point.x += tracked_segment_obstacles_[idx].getObstacle().first_point.x / tracked_segment_obstacles_[idx].getKFx1().P(0,0); + c.first_point.y += tracked_segment_obstacles_[idx].getObstacle().first_point.y / tracked_segment_obstacles_[idx].getKFy1().P(0,0); + c.last_point.x += tracked_segment_obstacles_[idx].getObstacle().last_point.x / tracked_segment_obstacles_[idx].getKFx2().P(0,0); + c.last_point.y += tracked_segment_obstacles_[idx].getObstacle().last_point.y / tracked_segment_obstacles_[idx].getKFy2().P(0,0); + c.first_velocity.x += tracked_segment_obstacles_[idx].getObstacle().first_velocity.x / tracked_segment_obstacles_[idx].getKFx1().P(1,1); + c.first_velocity.y += tracked_segment_obstacles_[idx].getObstacle().first_velocity.y / tracked_segment_obstacles_[idx].getKFy1().P(1,1); + c.last_velocity.x += tracked_segment_obstacles_[idx].getObstacle().last_velocity.x / tracked_segment_obstacles_[idx].getKFx2().P(1,1); + c.last_velocity.y += tracked_segment_obstacles_[idx].getObstacle().last_velocity.y / tracked_segment_obstacles_[idx].getKFy2().P(1,1); + + sum_var_x1 += 1.0 / tracked_segment_obstacles_[idx].getKFx1().P(0,0); + sum_var_y1 += 1.0 / tracked_segment_obstacles_[idx].getKFy1().P(0,0); + sum_var_x2 += 1.0 / tracked_segment_obstacles_[idx].getKFx2().P(0,0); + sum_var_y2 += 1.0 / tracked_segment_obstacles_[idx].getKFy2().P(0,0); + sum_var_vx1 += 1.0 / tracked_segment_obstacles_[idx].getKFx1().P(1,1); + sum_var_vy1 += 1.0 / tracked_segment_obstacles_[idx].getKFy1().P(1,1); + sum_var_vx2 += 1.0 / tracked_segment_obstacles_[idx].getKFx2().P(1,1); + sum_var_vy2 += 1.0 / tracked_segment_obstacles_[idx].getKFy2().P(1,1); + } + + c.first_point.x /= sum_var_x1; + c.first_point.y /= sum_var_y1; + c.last_point.x /= sum_var_x2; + c.last_point.y /= sum_var_y2; + c.first_velocity.x /= sum_var_vx1; + c.first_velocity.y /= sum_var_vy1; + c.last_velocity.x /= sum_var_vx2; + c.last_velocity.y /= sum_var_vy2; + + TrackedSegmentObstacle to(c); + to.correctState(new_obstacles->segments[col_min_indices[fusion_indices.front()]]); + for (int i = 0; i < static_cast(p_loop_rate_ / p_sensor_rate_); ++i) + to.updateState(); + + new_tracked.push_back(to); +} + +void ObstacleTracker::fissureObstacle(const vector& fission_indices, const vector& row_min_indices, + vector& new_tracked, const Obstacles::ConstPtr& new_obstacles) { + // For each new obstacle taking part in fission create a tracked obstacle from the original old one and update it with the new one + for (int idx : fission_indices) { + TrackedCircleObstacle to = tracked_circle_obstacles_[row_min_indices[idx]]; + // Ensure the fissured obstacle gets a new id + to.setNewUid(); + to.correctState(new_obstacles->circles[idx]); + for (int i = 0; i < static_cast(p_loop_rate_ / p_sensor_rate_); ++i) + to.updateState(); + + new_tracked.push_back(to); + } +} + +void ObstacleTracker::fissureObstacle(const vector& fission_indices, const vector& row_min_indices, + vector& new_tracked, const Obstacles::ConstPtr& new_obstacles) { + // For each new obstacle taking part in fission create a tracked obstacle from the original old one and update it with the new one + for (int idx : fission_indices) { + TrackedSegmentObstacle to = tracked_segment_obstacles_[row_min_indices[idx]]; + // Ensure the fissured obstacle gets a new id + to.setNewUid(); + to.correctState(new_obstacles->segments[idx]); + for (int i = 0; i < static_cast(p_loop_rate_ / p_sensor_rate_); ++i) + to.updateState(); + + new_tracked.push_back(to); + } +} + +void ObstacleTracker::updateObstacles() { + for (int i = 0; i < tracked_circle_obstacles_.size(); ++i) { + if (!tracked_circle_obstacles_[i].hasFaded()) + tracked_circle_obstacles_[i].updateState(); + else + tracked_circle_obstacles_.erase(tracked_circle_obstacles_.begin() + i--); + } + + for (int i = 0; i < tracked_segment_obstacles_.size(); ++i) { + if (!tracked_segment_obstacles_[i].hasFaded()) + tracked_segment_obstacles_[i].updateState(); + else + tracked_segment_obstacles_.erase(tracked_segment_obstacles_.begin() + i--); + } +} + +void ObstacleTracker::publishObstacles() { + obstacle_detector::ObstaclesPtr obstacles_msg(new obstacle_detector::Obstacles); + + obstacles_.circles.clear(); + obstacles_.segments.clear(); + + for (auto& tracked_circle_obstacle : tracked_circle_obstacles_) { + CircleObstacle ob = tracked_circle_obstacle.getObstacle(); + ob.true_radius = ob.radius - radius_margin_; + // Compensate robot velocity from obstacle velocity + // Velocities are in robot's frame, x forward y leftwards + if (p_compensate_robot_velocity_) + { + double distance = sqrt(pow(ob.center.x, 2) + pow(ob.center.y, 2)); + double angle = atan2(ob.center.y, ob.center.x); + ob.velocity.x += odom_.twist.twist.linear.x - odom_.twist.twist.angular.z * distance * sin(angle); + ob.velocity.y += odom_.twist.twist.linear.y + odom_.twist.twist.angular.z * distance * cos(angle); + } + obstacles_.circles.push_back(ob); + } + for (auto& tracked_segment_obstacle : tracked_segment_obstacles_) { + SegmentObstacle ob = tracked_segment_obstacle.getObstacle(); + // Compensate robot velocity from obstacle velocity + // Velocities are in robot's frame, x forward y leftwards + if (p_compensate_robot_velocity_) + { + double distance_first = sqrt(pow(ob.first_point.x, 2) + pow(ob.first_point.y, 2)); + double distance_last = sqrt(pow(ob.last_point.x, 2) + pow(ob.last_point.y, 2)); + double angle_first = atan2(ob.first_point.y, ob.first_point.x); + double angle_last = atan2(ob.last_point.y, ob.last_point.x); + ob.first_velocity.x += odom_.twist.twist.linear.x - odom_.twist.twist.angular.z * distance_first * sin(angle_first); + ob.first_velocity.y += odom_.twist.twist.linear.y + odom_.twist.twist.angular.z * distance_first * cos(angle_first); + ob.last_velocity.x += odom_.twist.twist.linear.x - odom_.twist.twist.angular.z * distance_last * sin(angle_last); + ob.last_velocity.y += odom_.twist.twist.linear.y + odom_.twist.twist.angular.z * distance_last * cos(angle_last); + } + obstacles_.segments.push_back(ob); + } + + *obstacles_msg = obstacles_; + obstacles_msg->header.stamp = ros::Time::now(); + + obstacles_pub_.publish(obstacles_msg); +} + +// Ugly initialization of static members of tracked obstacles... +int TrackedCircleObstacle::s_fade_counter_size_ = 0; +double TrackedCircleObstacle::s_sampling_time_ = 100.0; +double TrackedCircleObstacle::s_process_variance_ = 0.0; +double TrackedCircleObstacle::s_process_rate_variance_ = 0.0; +double TrackedCircleObstacle::s_measurement_variance_ = 0.0; +int TrackedSegmentObstacle::s_fade_counter_size_ = 0; +double TrackedSegmentObstacle::s_sampling_time_ = 100.0; +double TrackedSegmentObstacle::s_process_variance_ = 0.0; +double TrackedSegmentObstacle::s_process_rate_variance_ = 0.0; +double TrackedSegmentObstacle::s_measurement_variance_ = 0.0; diff --git a/src/obstacle_detector/src/panels/obstacle_extractor_panel.cpp b/src/obstacle_detector/src/panels/obstacle_extractor_panel.cpp new file mode 100644 index 0000000..23f8810 --- /dev/null +++ b/src/obstacle_detector/src/panels/obstacle_extractor_panel.cpp @@ -0,0 +1,334 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2017, Poznan University of Technology + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Poznan University of Technology nor the names + * of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Author: Mateusz Przybyla + */ + +#include "obstacle_detector/panels/obstacle_extractor_panel.h" + +using namespace obstacle_detector; +using namespace std; + +ObstacleExtractorPanel::ObstacleExtractorPanel(QWidget* parent) : rviz::Panel(parent), nh_(""), nh_local_("obstacle_extractor") { + params_cli_ = nh_local_.serviceClient("params"); + getParams(); + + activate_checkbox_ = new QCheckBox("On/Off"); + use_scan_checkbox_ = new QCheckBox("Use scan"); + use_pcl_checkbox_ = new QCheckBox("Use PCL"); + use_split_merge_checkbox_ = new QCheckBox("Use split and merge"); + circ_from_visib_checkbox_ = new QCheckBox("Use visibles"); + discard_segments_checkbox_ = new QCheckBox("Discard segments"); + transform_coords_checkbox_ = new QCheckBox("Transform coordinates"); + + min_n_input_ = new QLineEdit(); + dist_prop_input_ = new QLineEdit(); + group_dist_input_ = new QLineEdit(); + split_dist_input_ = new QLineEdit(); + merge_sep_input_ = new QLineEdit(); + merge_spread_input_ = new QLineEdit(); + max_radius_input_ = new QLineEdit(); + radius_enl_input_ = new QLineEdit(); + frame_id_input_ = new QLineEdit(); + + min_n_input_->setAlignment(Qt::AlignRight); + dist_prop_input_->setAlignment(Qt::AlignRight); + group_dist_input_->setAlignment(Qt::AlignRight); + split_dist_input_->setAlignment(Qt::AlignRight); + merge_sep_input_->setAlignment(Qt::AlignRight); + merge_spread_input_->setAlignment(Qt::AlignRight); + max_radius_input_->setAlignment(Qt::AlignRight); + radius_enl_input_->setAlignment(Qt::AlignRight); + frame_id_input_->setAlignment(Qt::AlignRight); + + QFrame* lines[5]; + for (auto& line : lines) { + line = new QFrame(); + line->setFrameShape(QFrame::HLine); + line->setFrameShadow(QFrame::Sunken); + } + + QSpacerItem* margin = new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Fixed); + + QHBoxLayout* scan_pcl_layout = new QHBoxLayout; + scan_pcl_layout->addItem(margin); + scan_pcl_layout->addWidget(use_scan_checkbox_); + scan_pcl_layout->addItem(margin); + scan_pcl_layout->addWidget(use_pcl_checkbox_); + scan_pcl_layout->addItem(margin); + + QGroupBox* segmentation_box = new QGroupBox("Segmentation:"); + QGridLayout* segmentation_layout = new QGridLayout; + segmentation_layout->addWidget(new QLabel("Nmin:"), 0, 0, Qt::AlignRight); + segmentation_layout->addWidget(min_n_input_, 0, 1); + segmentation_layout->addWidget(new QLabel(" "), 0, 2); + segmentation_layout->addWidget(new QLabel("dp:"), 0, 3, Qt::AlignRight); + segmentation_layout->addWidget(dist_prop_input_, 0, 4); + segmentation_layout->addWidget(new QLabel(""), 0, 5); + + segmentation_layout->addWidget(new QLabel("dgroup:"), 1, 0, Qt::AlignRight); + segmentation_layout->addWidget(group_dist_input_, 1, 1); + segmentation_layout->addWidget(new QLabel("m "), 1, 2, Qt::AlignLeft); + segmentation_layout->addWidget(new QLabel("dsplit:"), 1, 3, Qt::AlignRight); + segmentation_layout->addWidget(split_dist_input_, 1, 4); + segmentation_layout->addWidget(new QLabel("m"), 1, 5, Qt::AlignLeft); + + segmentation_layout->addWidget(new QLabel("dsep:"), 2, 0, Qt::AlignRight); + segmentation_layout->addWidget(merge_sep_input_, 2, 1); + segmentation_layout->addWidget(new QLabel("m "), 2, 2, Qt::AlignLeft); + segmentation_layout->addWidget(new QLabel("dspread:"), 2, 3, Qt::AlignRight); + segmentation_layout->addWidget(merge_spread_input_, 2, 4); + segmentation_layout->addWidget(new QLabel("m"), 2, 5, Qt::AlignLeft); + + segmentation_layout->addWidget(use_split_merge_checkbox_, 3, 0, 1, 6, Qt::AlignCenter); + segmentation_box->setLayout(segmentation_layout); + + QGroupBox* circle_box = new QGroupBox("Circularization:"); + QGridLayout* circ_limits_layout = new QGridLayout; + circ_limits_layout->addWidget(new QLabel("rmax:"), 0, 0, Qt::AlignRight); + circ_limits_layout->addWidget(max_radius_input_, 0, 1); + circ_limits_layout->addWidget(new QLabel("m "), 0, 2, Qt::AlignLeft); + circ_limits_layout->addWidget(new QLabel("rmargin:"), 0, 3, Qt::AlignRight); + circ_limits_layout->addWidget(radius_enl_input_, 0, 4); + circ_limits_layout->addWidget(new QLabel("m"), 0, 5, Qt::AlignLeft); + + QHBoxLayout* circ_checks_layout = new QHBoxLayout; + circ_checks_layout->addWidget(discard_segments_checkbox_, 0, Qt::AlignCenter); + circ_checks_layout->addWidget(circ_from_visib_checkbox_, 0, Qt::AlignCenter); + + QVBoxLayout* circ_layout = new QVBoxLayout; + circ_layout->addLayout(circ_limits_layout); + circ_layout->addLayout(circ_checks_layout); + circle_box->setLayout(circ_layout); + + QGroupBox* frame_box = new QGroupBox("Frames:"); + QGridLayout* layout_4 = new QGridLayout; + layout_4->addItem(margin, 0, 0, 2, 1); + layout_4->addWidget(new QLabel("Target frame:"), 0, 1, Qt::AlignRight); + layout_4->addWidget(frame_id_input_, 0, 2, Qt::AlignLeft); + layout_4->addWidget(transform_coords_checkbox_, 1, 1, 1, 2, Qt::AlignCenter); + layout_4->addItem(margin, 0, 3, 2, 1); + frame_box->setLayout(layout_4); + + QVBoxLayout* layout = new QVBoxLayout; + layout->addWidget(activate_checkbox_); + layout->addWidget(lines[0]); + layout->addLayout(scan_pcl_layout); + layout->addWidget(lines[1]); + layout->addWidget(segmentation_box); + layout->addWidget(lines[2]); + layout->addWidget(circle_box); + layout->addWidget(lines[3]); + layout->addWidget(frame_box); + layout->setAlignment(layout, Qt::AlignCenter); + setLayout(layout); + + connect(activate_checkbox_, SIGNAL(clicked()), this, SLOT(processInputs())); + connect(use_scan_checkbox_, SIGNAL(clicked()), this, SLOT(processInputs())); + connect(use_pcl_checkbox_, SIGNAL(clicked()), this, SLOT(processInputs())); + connect(use_split_merge_checkbox_, SIGNAL(clicked()), this, SLOT(processInputs())); + connect(circ_from_visib_checkbox_, SIGNAL(clicked()), this, SLOT(processInputs())); + connect(discard_segments_checkbox_, SIGNAL(clicked()), this, SLOT(processInputs())); + connect(transform_coords_checkbox_, SIGNAL(clicked()), this, SLOT(processInputs())); + + connect(min_n_input_, SIGNAL(editingFinished()), this, SLOT(processInputs())); + connect(dist_prop_input_, SIGNAL(editingFinished()), this, SLOT(processInputs())); + connect(group_dist_input_, SIGNAL(editingFinished()), this, SLOT(processInputs())); + connect(split_dist_input_, SIGNAL(editingFinished()), this, SLOT(processInputs())); + connect(merge_sep_input_, SIGNAL(editingFinished()), this, SLOT(processInputs())); + connect(merge_spread_input_, SIGNAL(editingFinished()), this, SLOT(processInputs())); + connect(max_radius_input_, SIGNAL(editingFinished()), this, SLOT(processInputs())); + connect(radius_enl_input_, SIGNAL(editingFinished()), this, SLOT(processInputs())); + connect(frame_id_input_, SIGNAL(editingFinished()), this, SLOT(processInputs())); + + evaluateParams(); +} + +void ObstacleExtractorPanel::processInputs() { + verifyInputs(); + setParams(); + evaluateParams(); + notifyParamsUpdate(); +} + +void ObstacleExtractorPanel::verifyInputs() { + p_active_ = activate_checkbox_->isChecked(); + p_use_scan_ = use_scan_checkbox_->isChecked(); + p_use_pcl_ = use_pcl_checkbox_->isChecked(); + + p_use_split_and_merge_ = use_split_merge_checkbox_->isChecked(); + p_circles_from_visibles_ = circ_from_visib_checkbox_->isChecked(); + p_discard_converted_segments_ = discard_segments_checkbox_->isChecked(); + p_transform_coordinates_ = transform_coords_checkbox_->isChecked(); + + try { p_min_group_points_ = boost::lexical_cast(min_n_input_->text().toStdString()); } + catch(boost::bad_lexical_cast &) { p_min_group_points_ = 0; min_n_input_->setText("0"); } + + try { p_distance_proportion_ = boost::lexical_cast(dist_prop_input_->text().toStdString()); } + catch(boost::bad_lexical_cast &) { p_distance_proportion_ = 0.0; dist_prop_input_->setText("0.0"); } + + try { p_max_group_distance_ = boost::lexical_cast(group_dist_input_->text().toStdString()); } + catch(boost::bad_lexical_cast &) { p_max_group_distance_ = 0.0; group_dist_input_->setText("0.0"); } + + try { p_max_split_distance_ = boost::lexical_cast(split_dist_input_->text().toStdString()); } + catch(boost::bad_lexical_cast &) { p_max_split_distance_ = 0.0; split_dist_input_->setText("0.0"); } + + try { p_max_merge_separation_ = boost::lexical_cast(merge_sep_input_->text().toStdString()); } + catch(boost::bad_lexical_cast &) { p_max_merge_separation_ = 0.0; merge_sep_input_->setText("0.0"); } + + try { p_max_merge_spread_ = boost::lexical_cast(merge_spread_input_->text().toStdString()); } + catch(boost::bad_lexical_cast &) { p_max_merge_spread_ = 0.0; merge_spread_input_->setText("0.0"); } + + try { p_max_circle_radius_ = boost::lexical_cast(max_radius_input_->text().toStdString()); } + catch(boost::bad_lexical_cast &) { p_max_circle_radius_ = 0.0; max_radius_input_->setText("0.0"); } + + try { p_radius_enlargement_ = boost::lexical_cast(radius_enl_input_->text().toStdString()); } + catch(boost::bad_lexical_cast &) { p_radius_enlargement_ = 0.0; radius_enl_input_->setText("0.0"); } + + p_frame_id_ = frame_id_input_->text().toStdString(); +} + +void ObstacleExtractorPanel::setParams() { + nh_local_.setParam("active", p_active_); + nh_local_.setParam("use_scan", p_use_scan_); + nh_local_.setParam("use_pcl", p_use_pcl_); + + nh_local_.setParam("use_split_and_merge", p_use_split_and_merge_); + nh_local_.setParam("circles_from_visibles", p_circles_from_visibles_); + nh_local_.setParam("discard_converted_segments", p_discard_converted_segments_); + nh_local_.setParam("transform_coordinates", p_transform_coordinates_); + + nh_local_.setParam("min_group_points", p_min_group_points_); + + nh_local_.setParam("max_group_distance", p_max_group_distance_); + nh_local_.setParam("distance_proportion", p_distance_proportion_); + nh_local_.setParam("max_split_distance", p_max_split_distance_); + + nh_local_.setParam("max_merge_separation", p_max_merge_separation_); + nh_local_.setParam("max_merge_spread", p_max_merge_spread_); + nh_local_.setParam("max_circle_radius", p_max_circle_radius_); + nh_local_.setParam("radius_enlargement", p_radius_enlargement_); + + nh_local_.setParam("frame_id", p_frame_id_); +} + +void ObstacleExtractorPanel::getParams() { + p_active_ = nh_local_.param("active", false); + p_use_scan_ = nh_local_.param("use_scan", false); + p_use_pcl_ = nh_local_.param("use_pcl", false); + + p_use_split_and_merge_ = nh_local_.param("use_split_and_merge", false); + p_circles_from_visibles_ = nh_local_.param("circles_from_visibles", false); + p_discard_converted_segments_ = nh_local_.param("discard_converted_segments", false); + p_transform_coordinates_ = nh_local_.param("transform_coordinates", false); + + p_min_group_points_ = nh_local_.param("min_group_points", 0); + + p_max_group_distance_ = nh_local_.param("max_group_distance", 0.0); + p_distance_proportion_ = nh_local_.param("distance_proportion", 0.0); + p_max_split_distance_ = nh_local_.param("max_split_distance", 0.0); + p_max_merge_separation_ = nh_local_.param("max_merge_separation", 0.0); + p_max_merge_spread_ = nh_local_.param("max_merge_spread", 0.0); + p_max_circle_radius_ = nh_local_.param("max_circle_radius", 0.0); + p_radius_enlargement_ = nh_local_.param("radius_enlargement", 0.0); + + p_frame_id_ = nh_local_.param("frame_id", std::string("")); +} + +void ObstacleExtractorPanel::evaluateParams() { + activate_checkbox_->setChecked(p_active_); + + use_scan_checkbox_->setEnabled(p_active_); + use_scan_checkbox_->setChecked(p_use_scan_); + + use_pcl_checkbox_->setEnabled(p_active_); + use_pcl_checkbox_->setChecked(p_use_pcl_); + + use_split_merge_checkbox_->setEnabled(p_active_); + use_split_merge_checkbox_->setChecked(p_use_split_and_merge_); + + circ_from_visib_checkbox_->setEnabled(p_active_); + circ_from_visib_checkbox_->setChecked(p_circles_from_visibles_); + + discard_segments_checkbox_->setEnabled(p_active_); + discard_segments_checkbox_->setChecked(p_discard_converted_segments_); + + transform_coords_checkbox_->setEnabled(p_active_); + transform_coords_checkbox_->setChecked(p_transform_coordinates_); + + min_n_input_->setEnabled(p_active_); + min_n_input_->setText(QString::number(p_min_group_points_)); + + dist_prop_input_->setEnabled(p_active_); + dist_prop_input_->setText(QString::number(p_distance_proportion_)); + + group_dist_input_->setEnabled(p_active_); + group_dist_input_->setText(QString::number(p_max_group_distance_)); + + split_dist_input_->setEnabled(p_active_); + split_dist_input_->setText(QString::number(p_max_split_distance_)); + + merge_sep_input_->setEnabled(p_active_); + merge_sep_input_->setText(QString::number(p_max_merge_separation_)); + + merge_spread_input_->setEnabled(p_active_); + merge_spread_input_->setText(QString::number(p_max_merge_spread_)); + + max_radius_input_->setEnabled(p_active_); + max_radius_input_->setText(QString::number(p_max_circle_radius_)); + + radius_enl_input_->setEnabled(p_active_); + radius_enl_input_->setText(QString::number(p_radius_enlargement_)); + + frame_id_input_->setEnabled(p_active_); + frame_id_input_->setText(QString::fromStdString(p_frame_id_)); +} + +void ObstacleExtractorPanel::notifyParamsUpdate() { + std_srvs::Empty empty; + if (!params_cli_.call(empty)) { + p_active_ = false; + setParams(); + evaluateParams(); + } +} + +void ObstacleExtractorPanel::save(rviz::Config config) const { + rviz::Panel::save(config); +} + +void ObstacleExtractorPanel::load(const rviz::Config& config) { + rviz::Panel::load(config); +} + +#include +PLUGINLIB_EXPORT_CLASS(obstacle_detector::ObstacleExtractorPanel, rviz::Panel) diff --git a/src/obstacle_detector/src/panels/obstacle_publisher_panel.cpp b/src/obstacle_detector/src/panels/obstacle_publisher_panel.cpp new file mode 100644 index 0000000..8bc9b58 --- /dev/null +++ b/src/obstacle_detector/src/panels/obstacle_publisher_panel.cpp @@ -0,0 +1,328 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2017, Poznan University of Technology + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Poznan University of Technology nor the names + * of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Author: Mateusz Przybyla + */ + +#include "obstacle_detector/panels/obstacle_publisher_panel.h" + +using namespace obstacle_detector; +using namespace std; + +ObstaclePublisherPanel::ObstaclePublisherPanel(QWidget* parent) : rviz::Panel(parent), nh_(""), nh_local_("obstacle_publisher") { + params_cli_ = nh_local_.serviceClient("params"); + getParams(); + + activate_checkbox_ = new QCheckBox("On/Off"); + fusion_example_checkbox_ = new QCheckBox("Fusion"); + fission_example_checkbox_ = new QCheckBox("Fission"); + + obstacles_list_ = new QListWidget(); + add_button_ = new QPushButton("Add obstacle"); + remove_button_ = new QPushButton("Remove selected"); + reset_button_ = new QPushButton("Reset time"); + + x_input_ = new QLineEdit(); + y_input_ = new QLineEdit(); + r_input_ = new QLineEdit(); + vx_input_ = new QLineEdit(); + vy_input_ = new QLineEdit(); + frame_input_ = new QLineEdit(); + + obstacles_list_->setSelectionMode(QAbstractItemView::MultiSelection); + frame_input_->setAlignment(Qt::AlignRight); + + QFrame* lines[5]; + for (auto& line : lines) { + line = new QFrame(); + line->setFrameShape(QFrame::HLine); + line->setFrameShadow(QFrame::Sunken); + } + + QSpacerItem* margin = new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Fixed); + + QHBoxLayout* demos_layout = new QHBoxLayout; + demos_layout->addWidget(fusion_example_checkbox_); + demos_layout->addWidget(fission_example_checkbox_); + + QGroupBox* demos_box = new QGroupBox("Demos:"); + demos_box->setLayout(demos_layout); + + QHBoxLayout* target_frame_layout = new QHBoxLayout; + target_frame_layout->addItem(margin); + target_frame_layout->addWidget(new QLabel("Target frame:")); + target_frame_layout->addWidget(frame_input_, 0, Qt::AlignLeft); + target_frame_layout->addItem(margin); + + QGroupBox* frames_box = new QGroupBox("Frames:"); + frames_box->setLayout(target_frame_layout); + + QHBoxLayout* xyr_layout = new QHBoxLayout; + xyr_layout->addItem(margin); + xyr_layout->addWidget(new QLabel("x:")); + xyr_layout->addWidget(x_input_); + xyr_layout->addWidget(new QLabel("m, ")); + xyr_layout->addWidget(new QLabel("y:")); + xyr_layout->addWidget(y_input_); + xyr_layout->addWidget(new QLabel("m, ")); + xyr_layout->addWidget(new QLabel("r:")); + xyr_layout->addWidget(r_input_); + xyr_layout->addWidget(new QLabel("m")); + xyr_layout->addItem(margin); + + QHBoxLayout* vxvy_layout = new QHBoxLayout; + vxvy_layout->addItem(margin); + vxvy_layout->addWidget(new QLabel("vx:")); + vxvy_layout->addWidget(vx_input_); + vxvy_layout->addWidget(new QLabel("m/s, ")); + vxvy_layout->addWidget(new QLabel("vy:")); + vxvy_layout->addWidget(vy_input_); + vxvy_layout->addWidget(new QLabel("m/s")); + vxvy_layout->addItem(margin); + + QVBoxLayout* obst_params_layout = new QVBoxLayout; + obst_params_layout->addWidget(obstacles_list_); + obst_params_layout->addWidget(remove_button_); + obst_params_layout->addWidget(lines[2]); + obst_params_layout->addLayout(xyr_layout); + obst_params_layout->addLayout(vxvy_layout); + obst_params_layout->addWidget(add_button_, Qt::AlignCenter); + obst_params_layout->addWidget(lines[3]); + obst_params_layout->addWidget(reset_button_, Qt::AlignCenter); + + QGroupBox* obst_box = new QGroupBox("Obstacles:"); + obst_box->setLayout(obst_params_layout); + + QVBoxLayout* layout = new QVBoxLayout; + layout->addWidget(activate_checkbox_); + layout->addWidget(lines[0]); + layout->addWidget(demos_box); + layout->addWidget(lines[1]); + layout->addWidget(frames_box); + layout->addWidget(lines[4]); + layout->addWidget(obst_box); + layout->setAlignment(layout, Qt::AlignCenter); + setLayout(layout); + + connect(activate_checkbox_, SIGNAL(clicked()), this, SLOT(processInputs())); + connect(fusion_example_checkbox_, SIGNAL(clicked()), this, SLOT(processInputs())); + connect(fission_example_checkbox_, SIGNAL(clicked()), this, SLOT(processInputs())); + connect(frame_input_, SIGNAL(editingFinished()), this, SLOT(processInputs())); + + connect(add_button_, SIGNAL(clicked()), this, SLOT(addObstacle())); + connect(remove_button_, SIGNAL(clicked()), this, SLOT(removeObstacles())); + connect(reset_button_, SIGNAL(clicked()), this, SLOT(reset())); + + evaluateParams(); +} + +void ObstaclePublisherPanel::processInputs() { + verifyInputs(); + setParams(); + evaluateParams(); + notifyParamsUpdate(); +} + +void ObstaclePublisherPanel::addObstacle() { + verifyInputs(); + + if (r_ > 0.0) { + p_x_vector_.push_back(x_); + p_y_vector_.push_back(y_); + p_r_vector_.push_back(r_); + + p_vx_vector_.push_back(vx_); + p_vy_vector_.push_back(vy_); + + setParams(); + evaluateParams(); + notifyParamsUpdate(); + } +} + +void ObstaclePublisherPanel::removeObstacles() { + QModelIndexList indexes = obstacles_list_->selectionModel()->selectedIndexes(); + + vector index_list; + for (QModelIndex index : indexes) + index_list.push_back(index.row()); + + sort(index_list.begin(), index_list.end(), greater()); + + for (int idx : index_list) { + p_x_vector_.erase(p_x_vector_.begin() + idx); + p_y_vector_.erase(p_y_vector_.begin() + idx); + p_r_vector_.erase(p_r_vector_.begin() + idx); + + p_vx_vector_.erase(p_vx_vector_.begin() + idx); + p_vy_vector_.erase(p_vy_vector_.begin() + idx); + + delete obstacles_list_items_[idx]; + obstacles_list_items_.erase(obstacles_list_items_.begin() + idx); + } + + setParams(); + evaluateParams(); + notifyParamsUpdate(); +} + +void ObstaclePublisherPanel::reset() { + p_reset_ = true; + + processInputs(); + + p_reset_ = false; +} + +void ObstaclePublisherPanel::verifyInputs() { + p_active_ = activate_checkbox_->isChecked(); + p_fusion_example_ = fusion_example_checkbox_->isChecked(); + p_fission_example_ = fission_example_checkbox_->isChecked(); + + try { x_ = boost::lexical_cast(x_input_->text().toStdString()); } + catch(boost::bad_lexical_cast &) { x_ = 0.0; x_input_->setText("0.0"); } + + try { y_ = boost::lexical_cast(y_input_->text().toStdString()); } + catch(boost::bad_lexical_cast &) { y_ = 0.0; y_input_->setText("0.0"); } + + try { r_ = boost::lexical_cast(r_input_->text().toStdString()); } + catch(boost::bad_lexical_cast &) { r_ = 0.0; r_input_->setText("0.0"); } + + try { vx_ = boost::lexical_cast(vx_input_->text().toStdString()); } + catch(boost::bad_lexical_cast &) { vx_ = 0.0; vx_input_->setText("0.0"); } + + try { vy_ = boost::lexical_cast(vy_input_->text().toStdString()); } + catch(boost::bad_lexical_cast &) { vy_ = 0.0; vy_input_->setText("0.0"); } + + p_frame_id_ = frame_input_->text().toStdString(); +} + +void ObstaclePublisherPanel::setParams() { + nh_local_.setParam("active", p_active_); + nh_local_.setParam("reset", p_reset_); + + nh_local_.setParam("fusion_example", p_fusion_example_); + nh_local_.setParam("fission_example", p_fission_example_); + + nh_local_.setParam("x_vector", p_x_vector_); + nh_local_.setParam("y_vector", p_y_vector_); + nh_local_.setParam("r_vector", p_r_vector_); + + nh_local_.setParam("vx_vector", p_vx_vector_); + nh_local_.setParam("vy_vector", p_vy_vector_); + + nh_local_.setParam("frame_id", p_frame_id_); +} + +void ObstaclePublisherPanel::getParams() { + p_active_ = nh_local_.param("active", false); + p_reset_ = nh_local_.param("reset", false); + + p_fusion_example_ = nh_local_.param("fusion_example", false); + p_fission_example_ = nh_local_.param("fission_example", false); + + nh_local_.getParam("x_vector", p_x_vector_); + nh_local_.getParam("y_vector", p_y_vector_); + nh_local_.getParam("r_vector", p_r_vector_); + + nh_local_.getParam("vx_vector", p_vx_vector_); + nh_local_.getParam("vy_vector", p_vy_vector_); + + p_frame_id_ = nh_local_.param("frame_id", std::string("")); +} + +void ObstaclePublisherPanel::evaluateParams() { + activate_checkbox_->setChecked(p_active_); + fusion_example_checkbox_->setEnabled(p_active_); + fission_example_checkbox_->setEnabled(p_active_); + + fusion_example_checkbox_->setChecked(p_fusion_example_); + fission_example_checkbox_->setChecked(p_fission_example_); + + frame_input_->setEnabled(p_active_); + frame_input_->setText(QString::fromStdString(p_frame_id_)); + + add_button_->setEnabled(p_active_); + remove_button_->setEnabled(p_active_); + reset_button_->setEnabled(p_active_); + + x_input_->setEnabled(p_active_); + y_input_->setEnabled(p_active_); + r_input_->setEnabled(p_active_); + + vx_input_->setEnabled(p_active_); + vy_input_->setEnabled(p_active_); + + obstacles_list_->setEnabled(p_active_); + + if (p_x_vector_.size() < p_y_vector_.size() || p_x_vector_.size() < p_r_vector_.size() || + p_x_vector_.size() < p_vx_vector_.size() || p_x_vector_.size() < p_vy_vector_.size()) + return; + + for (QListWidgetItem* item : obstacles_list_items_) + delete item; + + obstacles_list_items_.clear(); + obstacles_list_->clear(); + + for (int idx = 0; idx < p_x_vector_.size(); ++idx) { + QListWidgetItem* item = new QListWidgetItem; + item->setText(QString::number(idx + 1) + ". " + + "[x: " + QString::number(p_x_vector_[idx]) + "m] " + + "[y: " + QString::number(p_y_vector_[idx]) + "m] " + + "[r: " + QString::number(p_r_vector_[idx]) + "m] " + + "[vx: " + QString::number(p_vx_vector_[idx]) + "m/s] " + + "[vy: " + QString::number(p_vy_vector_[idx]) + "m/s]"); + obstacles_list_items_.push_back(item); + obstacles_list_->insertItem(idx, item); + } +} + +void ObstaclePublisherPanel::notifyParamsUpdate() { + std_srvs::Empty empty; + if (!params_cli_.call(empty)) { + p_active_ = false; + setParams(); + evaluateParams(); + } +} + +void ObstaclePublisherPanel::save(rviz::Config config) const { + rviz::Panel::save(config); +} + +void ObstaclePublisherPanel::load(const rviz::Config& config) { + rviz::Panel::load(config); +} + +#include +PLUGINLIB_EXPORT_CLASS(obstacle_detector::ObstaclePublisherPanel, rviz::Panel) diff --git a/src/obstacle_detector/src/panels/obstacle_tracker_panel.cpp b/src/obstacle_detector/src/panels/obstacle_tracker_panel.cpp new file mode 100644 index 0000000..8e5a481 --- /dev/null +++ b/src/obstacle_detector/src/panels/obstacle_tracker_panel.cpp @@ -0,0 +1,228 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2017, Poznan University of Technology + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Poznan University of Technology nor the names + * of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Author: Mateusz Przybyla + */ + +#include "obstacle_detector/panels/obstacle_tracker_panel.h" + +using namespace obstacle_detector; +using namespace std; + +ObstacleTrackerPanel::ObstacleTrackerPanel(QWidget* parent) : rviz::Panel(parent), nh_(""), nh_local_("obstacle_tracker") { + params_cli_ = nh_local_.serviceClient("params"); + getParams(); + + activate_checkbox_ = new QCheckBox("On/Off"); + + tracking_duration_input_ = new QLineEdit(); + loop_rate_input_ = new QLineEdit(); + min_corr_cost_input_ = new QLineEdit(); + std_corr_dev_input_ = new QLineEdit(); + process_var_input_ = new QLineEdit(); + process_rate_var_input_ = new QLineEdit(); + measure_var_input_ = new QLineEdit(); + + tracking_duration_input_->setAlignment(Qt::AlignRight); + loop_rate_input_->setAlignment(Qt::AlignRight); + min_corr_cost_input_->setAlignment(Qt::AlignRight); + std_corr_dev_input_->setAlignment(Qt::AlignRight); + process_var_input_->setAlignment(Qt::AlignRight); + process_rate_var_input_->setAlignment(Qt::AlignRight); + measure_var_input_->setAlignment(Qt::AlignRight); + + QSpacerItem* margin = new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Fixed); + + QFrame* lines[4]; + for (auto& line : lines) { + line = new QFrame(); + line->setFrameShape(QFrame::HLine); + line->setFrameShadow(QFrame::Sunken); + } + + QGroupBox* tracking_box = new QGroupBox("General:"); + QGridLayout* tracking_layout = new QGridLayout; + tracking_layout->addWidget(new QLabel("Tracking rate:"), 0, 0, Qt::AlignRight); + tracking_layout->addWidget(loop_rate_input_, 0, 1, Qt::AlignLeft); + tracking_layout->addWidget(new QLabel("Hz"), 0, 2, Qt::AlignLeft); + tracking_layout->addWidget(new QLabel("Tracking duration:"), 1, 0, Qt::AlignRight); + tracking_layout->addWidget(tracking_duration_input_, 1, 1, Qt::AlignLeft); + tracking_layout->addWidget(new QLabel("s"), 1, 2, Qt::AlignLeft); + tracking_box->setLayout(tracking_layout); + + QGroupBox* corr_box = new QGroupBox("Correspondence:"); + QGridLayout* corr_layout = new QGridLayout; + corr_layout->addWidget(new QLabel("Minimal cost:"), 0, 0, Qt::AlignRight); + corr_layout->addWidget(min_corr_cost_input_, 0, 1, Qt::AlignLeft); + corr_layout->addWidget(new QLabel("m"), 0, 2, Qt::AlignLeft); + corr_layout->addWidget(new QLabel("Standard deviation:"), 1, 0, Qt::AlignRight); + corr_layout->addWidget(std_corr_dev_input_, 1, 1, Qt::AlignLeft); + corr_layout->addWidget(new QLabel("m"), 1, 2, Qt::AlignLeft); + corr_box->setLayout(corr_layout); + + QGroupBox* kf_box = new QGroupBox("Kalman Filter:"); + QGridLayout* kf_layout = new QGridLayout; + kf_layout->addWidget(new QLabel("Process variance:"), 0, 0, Qt::AlignRight); + kf_layout->addWidget(process_var_input_, 0, 1, Qt::AlignLeft); + kf_layout->addWidget(new QLabel("m2"), 0, 2, Qt::AlignLeft); + kf_layout->addWidget(new QLabel("Process rate variance:"), 1, 0, Qt::AlignRight); + kf_layout->addWidget(process_rate_var_input_, 1, 1, Qt::AlignLeft); + kf_layout->addWidget(new QLabel("(m/s)2"), 1, 2, Qt::AlignLeft); + kf_layout->addWidget(new QLabel("Measurement variance:"), 2, 0, Qt::AlignRight); + kf_layout->addWidget(measure_var_input_, 2, 1, Qt::AlignLeft); + kf_layout->addWidget(new QLabel("m2"), 2, 2, Qt::AlignLeft); + kf_box->setLayout(kf_layout); + + QVBoxLayout* layout = new QVBoxLayout; + layout->addWidget(activate_checkbox_); + layout->addWidget(lines[0]); + layout->addWidget(tracking_box); + layout->addWidget(lines[1]); + layout->addWidget(corr_box); + layout->addWidget(lines[2]); + layout->addWidget(kf_box); + layout->setAlignment(layout, Qt::AlignCenter); + setLayout(layout); + + connect(activate_checkbox_, SIGNAL(clicked()), this, SLOT(processInputs())); + + connect(tracking_duration_input_, SIGNAL(editingFinished()), this, SLOT(processInputs())); + connect(loop_rate_input_, SIGNAL(editingFinished()), this, SLOT(processInputs())); + connect(min_corr_cost_input_, SIGNAL(editingFinished()), this, SLOT(processInputs())); + connect(std_corr_dev_input_, SIGNAL(editingFinished()), this, SLOT(processInputs())); + connect(process_var_input_, SIGNAL(editingFinished()), this, SLOT(processInputs())); + connect(process_rate_var_input_, SIGNAL(editingFinished()), this, SLOT(processInputs())); + connect(measure_var_input_, SIGNAL(editingFinished()), this, SLOT(processInputs())); + + evaluateParams(); +} + +void ObstacleTrackerPanel::processInputs() { + verifyInputs(); + setParams(); + evaluateParams(); + notifyParamsUpdate(); +} + +void ObstacleTrackerPanel::verifyInputs() { + p_active_ = activate_checkbox_->isChecked(); + + try { p_loop_rate_ = boost::lexical_cast(loop_rate_input_->text().toStdString()); } + catch(boost::bad_lexical_cast &) { p_loop_rate_ = 1.0; loop_rate_input_->setText("1.0"); } + + try { p_tracking_duration_ = boost::lexical_cast(tracking_duration_input_->text().toStdString()); } + catch(boost::bad_lexical_cast &) { p_tracking_duration_ = 0.0; tracking_duration_input_->setText("0.0"); } + + try { p_min_correspondence_cost_ = boost::lexical_cast(min_corr_cost_input_->text().toStdString()); } + catch(boost::bad_lexical_cast &) { p_min_correspondence_cost_ = 0.0; min_corr_cost_input_->setText("0.0"); } + + try { p_std_correspondence_dev_ = boost::lexical_cast(std_corr_dev_input_->text().toStdString()); } + catch(boost::bad_lexical_cast &) { p_std_correspondence_dev_ = 0.0; std_corr_dev_input_->setText("0.0"); } + + try { p_process_variance_ = boost::lexical_cast(process_var_input_->text().toStdString()); } + catch(boost::bad_lexical_cast &) { p_process_variance_ = 0.0; process_var_input_->setText("0.0"); } + + try { p_process_rate_variance_ = boost::lexical_cast(process_rate_var_input_->text().toStdString()); } + catch(boost::bad_lexical_cast &) { p_process_rate_variance_ = 0.0; process_rate_var_input_->setText("0.0"); } + + try { p_measurement_variance_ = boost::lexical_cast(measure_var_input_->text().toStdString()); } + catch(boost::bad_lexical_cast &) { p_measurement_variance_ = 0.0; measure_var_input_->setText("0.0"); } +} + +void ObstacleTrackerPanel::setParams() { + nh_local_.setParam("active", p_active_); + + nh_local_.setParam("loop_rate", p_loop_rate_); + nh_local_.setParam("tracking_duration", p_tracking_duration_); + nh_local_.setParam("min_correspondence_cost", p_min_correspondence_cost_); + nh_local_.setParam("std_correspondence_dev", p_std_correspondence_dev_); + nh_local_.setParam("process_variance", p_process_variance_); + nh_local_.setParam("process_rate_variance", p_process_rate_variance_); + nh_local_.setParam("measurement_variance", p_measurement_variance_); +} + +void ObstacleTrackerPanel::getParams() { + p_active_ = nh_local_.param("active", false); + + p_loop_rate_ = nh_local_.param("loop_rate", 0.0); + p_tracking_duration_ = nh_local_.param("tracking_duration", 0.0); + p_min_correspondence_cost_ = nh_local_.param("min_correspondence_cost", 0.0); + p_std_correspondence_dev_ = nh_local_.param("std_correspondence_dev", 0.0); + p_process_variance_ = nh_local_.param("process_variance", 0.0); + p_process_rate_variance_ = nh_local_.param("process_rate_variance", 0.0); + p_measurement_variance_ = nh_local_.param("measurement_variance", 0.0); +} + +void ObstacleTrackerPanel::evaluateParams() { + activate_checkbox_->setChecked(p_active_); + + loop_rate_input_->setEnabled(p_active_); + loop_rate_input_->setText(QString::number(p_loop_rate_)); + + tracking_duration_input_->setEnabled(p_active_); + tracking_duration_input_->setText(QString::number(p_tracking_duration_)); + + min_corr_cost_input_->setEnabled(p_active_); + min_corr_cost_input_->setText(QString::number(p_min_correspondence_cost_)); + + std_corr_dev_input_->setEnabled(p_active_); + std_corr_dev_input_->setText(QString::number(p_std_correspondence_dev_)); + + process_var_input_->setEnabled(p_active_); + process_var_input_->setText(QString::number(p_process_variance_)); + + process_rate_var_input_->setEnabled(p_active_); + process_rate_var_input_->setText(QString::number(p_process_rate_variance_)); + + measure_var_input_->setEnabled(p_active_); + measure_var_input_->setText(QString::number(p_measurement_variance_)); +} + +void ObstacleTrackerPanel::notifyParamsUpdate() { + std_srvs::Empty empty; + if (!params_cli_.call(empty)) { + p_active_ = false; + setParams(); + evaluateParams(); + } +} + +void ObstacleTrackerPanel::save(rviz::Config config) const { + rviz::Panel::save(config); +} + +void ObstacleTrackerPanel::load(const rviz::Config& config) { + rviz::Panel::load(config); +} + +#include +PLUGINLIB_EXPORT_CLASS(obstacle_detector::ObstacleTrackerPanel, rviz::Panel) diff --git a/src/obstacle_detector/src/panels/scans_merger_panel.cpp b/src/obstacle_detector/src/panels/scans_merger_panel.cpp new file mode 100644 index 0000000..8aab6ab --- /dev/null +++ b/src/obstacle_detector/src/panels/scans_merger_panel.cpp @@ -0,0 +1,308 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2017, Poznan University of Technology + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Poznan University of Technology nor the names + * of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Author: Mateusz Przybyla + */ + +#include "obstacle_detector/panels/scans_merger_panel.h" + +using namespace obstacle_detector; +using namespace std; + +ScansMergerPanel::ScansMergerPanel(QWidget* parent) : rviz::Panel(parent), nh_(""), nh_local_("scans_merger") { + params_cli_ = nh_local_.serviceClient("params"); + getParams(); + + activate_checkbox_ = new QCheckBox("On/Off"); + scan_checkbox_ = new QCheckBox("Publish scan"); + pcl_checkbox_ = new QCheckBox("Publish PCL"); + + n_input_ = new QLineEdit(); + r_min_input_ = new QLineEdit(); + r_max_input_ = new QLineEdit(); + x_min_input_ = new QLineEdit(); + x_max_input_ = new QLineEdit(); + y_min_input_ = new QLineEdit(); + y_max_input_ = new QLineEdit(); + fixed_frame_id_input_ = new QLineEdit(); + target_frame_id_input_ = new QLineEdit(); + + n_input_->setAlignment(Qt::AlignRight); + r_min_input_->setAlignment(Qt::AlignRight); + r_max_input_->setAlignment(Qt::AlignRight); + x_min_input_->setAlignment(Qt::AlignRight); + x_max_input_->setAlignment(Qt::AlignRight); + y_min_input_->setAlignment(Qt::AlignRight); + y_max_input_->setAlignment(Qt::AlignRight); + fixed_frame_id_input_->setAlignment(Qt::AlignRight); + target_frame_id_input_->setAlignment(Qt::AlignRight); + + QFrame* lines[5]; + for (auto& line : lines) { + line = new QFrame(); + line->setFrameShape(QFrame::HLine); + line->setFrameShadow(QFrame::Sunken); + } + + QSpacerItem* margin = new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Fixed); + + QHBoxLayout* scan_pcl_layout = new QHBoxLayout; + scan_pcl_layout->addItem(margin); + scan_pcl_layout->addWidget(scan_checkbox_); + scan_pcl_layout->addItem(margin); + scan_pcl_layout->addWidget(pcl_checkbox_); + scan_pcl_layout->addItem(margin); + + QHBoxLayout* beams_num_layout = new QHBoxLayout; + beams_num_layout->addItem(margin); + beams_num_layout->addWidget(new QLabel("Number of beams:")); + beams_num_layout->addWidget(n_input_); + beams_num_layout->addItem(margin); + + QHBoxLayout* ranges_lim_layout = new QHBoxLayout; + ranges_lim_layout->addItem(margin); + ranges_lim_layout->addWidget(new QLabel("rmin:"), 0, Qt::AlignRight); + ranges_lim_layout->addWidget(r_min_input_); + ranges_lim_layout->addWidget(new QLabel("m "), 0, Qt::AlignLeft); + ranges_lim_layout->addWidget(new QLabel("rmax:"), 0, Qt::AlignRight); + ranges_lim_layout->addWidget(r_max_input_); + ranges_lim_layout->addWidget(new QLabel("m"), 0, Qt::AlignLeft); + ranges_lim_layout->addItem(margin); + + QVBoxLayout* beams_layout = new QVBoxLayout; + beams_layout->addLayout(beams_num_layout); + beams_layout->addLayout(ranges_lim_layout); + + QGroupBox* scan_box = new QGroupBox("Beams limits:"); + scan_box->setLayout(beams_layout); + + QHBoxLayout* x_lim_layout = new QHBoxLayout; + x_lim_layout->addItem(margin); + x_lim_layout->addWidget(new QLabel("xmin:"), 0, Qt::AlignRight); + x_lim_layout->addWidget(x_min_input_); + x_lim_layout->addWidget(new QLabel("m "), 0, Qt::AlignLeft); + x_lim_layout->addWidget(new QLabel("xmax:"), 0, Qt::AlignRight); + x_lim_layout->addWidget(x_max_input_); + x_lim_layout->addWidget(new QLabel("m"), 0, Qt::AlignLeft); + x_lim_layout->addItem(margin); + + QHBoxLayout* y_lim_layout = new QHBoxLayout; + y_lim_layout->addItem(margin); + y_lim_layout->addWidget(new QLabel("ymin:"), 0, Qt::AlignRight); + y_lim_layout->addWidget(y_min_input_); + y_lim_layout->addWidget(new QLabel("m "), 0, Qt::AlignLeft); + y_lim_layout->addWidget(new QLabel("ymax:"), 0, Qt::AlignRight); + y_lim_layout->addWidget(y_max_input_); + y_lim_layout->addWidget(new QLabel("m"), 0, Qt::AlignLeft); + y_lim_layout->addItem(margin); + + QVBoxLayout* xy_lim_layout = new QVBoxLayout; + xy_lim_layout->addLayout(x_lim_layout); + xy_lim_layout->addLayout(y_lim_layout); + + QGroupBox* xy_lim_box = new QGroupBox("Coordinates limits:"); + xy_lim_box->setLayout(xy_lim_layout); + + QHBoxLayout* fixed_frame_layout = new QHBoxLayout; + fixed_frame_layout->addItem(margin); + fixed_frame_layout->addWidget(new QLabel("Fixed frame:")); + fixed_frame_layout->addWidget(fixed_frame_id_input_, 0, Qt::AlignLeft); + fixed_frame_layout->addItem(margin); + + QHBoxLayout* target_frame_layout = new QHBoxLayout; + target_frame_layout->addItem(margin); + target_frame_layout->addWidget(new QLabel("Target frame:")); + target_frame_layout->addWidget(target_frame_id_input_, 0, Qt::AlignLeft); + target_frame_layout->addItem(margin); + + QVBoxLayout* frames_layout = new QVBoxLayout; + frames_layout->addLayout(fixed_frame_layout); + frames_layout->addLayout(target_frame_layout); + + QGroupBox* frames_box = new QGroupBox("Frames:"); + frames_box->setLayout(frames_layout); + + QVBoxLayout* layout = new QVBoxLayout; + layout->addWidget(activate_checkbox_); + layout->addWidget(lines[0]); + layout->addLayout(scan_pcl_layout); + layout->addWidget(lines[1]); + layout->addWidget(scan_box); + layout->addWidget(lines[2]); + layout->addWidget(xy_lim_box); + layout->addWidget(lines[3]); + layout->addWidget(frames_box); + layout->setAlignment(layout, Qt::AlignCenter); + setLayout(layout); + + connect(activate_checkbox_, SIGNAL(clicked()), this, SLOT(processInputs())); + connect(scan_checkbox_, SIGNAL(clicked()), this, SLOT(processInputs())); + connect(pcl_checkbox_, SIGNAL(clicked()), this, SLOT(processInputs())); + + connect(n_input_, SIGNAL(editingFinished()), this, SLOT(processInputs())); + connect(r_min_input_, SIGNAL(editingFinished()), this, SLOT(processInputs())); + connect(r_max_input_, SIGNAL(editingFinished()), this, SLOT(processInputs())); + connect(x_min_input_, SIGNAL(editingFinished()), this, SLOT(processInputs())); + connect(x_max_input_, SIGNAL(editingFinished()), this, SLOT(processInputs())); + connect(y_min_input_, SIGNAL(editingFinished()), this, SLOT(processInputs())); + connect(y_max_input_, SIGNAL(editingFinished()), this, SLOT(processInputs())); + connect(fixed_frame_id_input_, SIGNAL(editingFinished()), this, SLOT(processInputs())); + connect(target_frame_id_input_, SIGNAL(editingFinished()), this, SLOT(processInputs())); + + evaluateParams(); +} + +void ScansMergerPanel::processInputs() { + verifyInputs(); + setParams(); + evaluateParams(); + notifyParamsUpdate(); +} + +void ScansMergerPanel::verifyInputs() { + p_active_ = activate_checkbox_->isChecked(); + p_publish_scan_ = scan_checkbox_->isChecked(); + p_publish_pcl_ = pcl_checkbox_->isChecked(); + + try { p_ranges_num_ = boost::lexical_cast(n_input_->text().toStdString()); } + catch(boost::bad_lexical_cast &) { p_ranges_num_ = 0; n_input_->setText("0"); } + + try { p_min_scanner_range_ = boost::lexical_cast(r_min_input_->text().toStdString()); } + catch(boost::bad_lexical_cast &) { p_min_scanner_range_ = 0.0; r_min_input_->setText("0.0"); } + + try { p_max_scanner_range_ = boost::lexical_cast(r_max_input_->text().toStdString()); } + catch(boost::bad_lexical_cast &) { p_max_scanner_range_ = 0.0; r_max_input_->setText("0.0"); } + + try { p_min_x_range_ = boost::lexical_cast(x_min_input_->text().toStdString()); } + catch(boost::bad_lexical_cast &) { p_min_x_range_ = 0.0; x_min_input_->setText("0.0"); } + + try { p_max_x_range_ = boost::lexical_cast(x_max_input_->text().toStdString()); } + catch(boost::bad_lexical_cast &) { p_max_x_range_ = 0.0; x_max_input_->setText("0.0"); } + + try { p_min_y_range_ = boost::lexical_cast(y_min_input_->text().toStdString()); } + catch(boost::bad_lexical_cast &) { p_min_y_range_ = 0.0; y_min_input_->setText("0.0"); } + + try { p_max_y_range_ = boost::lexical_cast(y_max_input_->text().toStdString()); } + catch(boost::bad_lexical_cast &) { p_max_y_range_ = 0.0; y_max_input_->setText("0.0"); } + + p_fixed_frame_id_ = fixed_frame_id_input_->text().toStdString(); + p_target_frame_id_ = target_frame_id_input_->text().toStdString(); +} + +void ScansMergerPanel::setParams() { + nh_local_.setParam("active", p_active_); + nh_local_.setParam("publish_scan", p_publish_scan_); + nh_local_.setParam("publish_pcl", p_publish_pcl_); + + nh_local_.setParam("ranges_num", p_ranges_num_); + + nh_local_.setParam("min_scanner_range", p_min_scanner_range_); + nh_local_.setParam("max_scanner_range", p_max_scanner_range_); + + nh_local_.setParam("min_x_range", p_min_x_range_); + nh_local_.setParam("max_x_range", p_max_x_range_); + nh_local_.setParam("min_y_range", p_min_y_range_); + nh_local_.setParam("max_y_range", p_max_y_range_); + + nh_local_.setParam("fixed_frame_id", p_fixed_frame_id_); + nh_local_.setParam("target_frame_id", p_target_frame_id_); +} + +void ScansMergerPanel::getParams() { + p_active_ = nh_local_.param("active", false); + p_publish_scan_ = nh_local_.param("publish_scan", false); + p_publish_pcl_ = nh_local_.param("publish_pcl", false); + + p_ranges_num_ = nh_local_.param("ranges_num", 0); + + p_min_scanner_range_ = nh_local_.param("min_scanner_range", 0.0); + p_max_scanner_range_ = nh_local_.param("max_scanner_range", 0.0); + + p_min_x_range_ = nh_local_.param("min_x_range", 0.0); + p_max_x_range_ = nh_local_.param("max_x_range", 0.0); + p_min_y_range_ = nh_local_.param("min_y_range", 0.0); + p_max_y_range_ = nh_local_.param("max_y_range", 0.0); + + p_fixed_frame_id_ = nh_local_.param("fixed_frame_id", std::string("")); + p_target_frame_id_ = nh_local_.param("target_frame_id", std::string("")); +} + +void ScansMergerPanel::evaluateParams() { + activate_checkbox_->setChecked(p_active_); + + scan_checkbox_->setEnabled(p_active_); + pcl_checkbox_->setEnabled(p_active_); + + scan_checkbox_->setChecked(p_publish_scan_); + pcl_checkbox_->setChecked(p_publish_pcl_); + + n_input_->setEnabled(p_active_); + r_min_input_->setEnabled(p_active_); + r_max_input_->setEnabled(p_active_); + x_min_input_->setEnabled(p_active_); + x_max_input_->setEnabled(p_active_); + y_min_input_->setEnabled(p_active_); + y_max_input_->setEnabled(p_active_); + fixed_frame_id_input_->setEnabled(p_active_); + target_frame_id_input_->setEnabled(p_active_); + + n_input_->setText(QString::number(p_ranges_num_)); + r_min_input_->setText(QString::number(p_min_scanner_range_)); + r_max_input_->setText(QString::number(p_max_scanner_range_)); + x_min_input_->setText(QString::number(p_min_x_range_)); + x_max_input_->setText(QString::number(p_max_x_range_)); + y_min_input_->setText(QString::number(p_min_y_range_)); + y_max_input_->setText(QString::number(p_max_y_range_)); + + fixed_frame_id_input_->setText(QString::fromStdString(p_fixed_frame_id_)); + target_frame_id_input_->setText(QString::fromStdString(p_target_frame_id_)); +} + +void ScansMergerPanel::notifyParamsUpdate() { + std_srvs::Empty empty; + if (!params_cli_.call(empty)) { + p_active_ = false; + setParams(); + evaluateParams(); + } +} + +void ScansMergerPanel::save(rviz::Config config) const { + rviz::Panel::save(config); +} + +void ScansMergerPanel::load(const rviz::Config& config) { + rviz::Panel::load(config); +} + +#include +PLUGINLIB_EXPORT_CLASS(obstacle_detector::ScansMergerPanel, rviz::Panel) diff --git a/src/obstacle_detector/src/scans_merger.cpp b/src/obstacle_detector/src/scans_merger.cpp new file mode 100644 index 0000000..4f2ad86 --- /dev/null +++ b/src/obstacle_detector/src/scans_merger.cpp @@ -0,0 +1,253 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2017, Poznan University of Technology + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Poznan University of Technology nor the names + * of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Author: Mateusz Przybyla + */ + +#include "obstacle_detector/scans_merger.h" + +using namespace obstacle_detector; +using namespace std; + +ScansMerger::ScansMerger(ros::NodeHandle& nh, ros::NodeHandle& nh_local) : nh_(nh), nh_local_(nh_local) { + p_active_ = false; + + front_scan_received_ = false; + rear_scan_received_ = false; + + front_scan_error_ = false; + rear_scan_error_ = false; + + params_srv_ = nh_local_.advertiseService("params", &ScansMerger::updateParams, this); + + initialize(); +} + +ScansMerger::~ScansMerger() { + nh_local_.deleteParam("active"); + nh_local_.deleteParam("publish_scan"); + nh_local_.deleteParam("publish_pcl"); + + nh_local_.deleteParam("ranges_num"); + + nh_local_.deleteParam("min_scanner_range"); + nh_local_.deleteParam("max_scanner_range"); + + nh_local_.deleteParam("min_x_range"); + nh_local_.deleteParam("max_x_range"); + nh_local_.deleteParam("min_y_range"); + nh_local_.deleteParam("max_y_range"); + + nh_local_.deleteParam("fixed_frame_id"); + nh_local_.deleteParam("target_frame_id"); +} + +bool ScansMerger::updateParams(std_srvs::Empty::Request &req, std_srvs::Empty::Response &res) { + bool prev_active = p_active_; + + nh_local_.param("active", p_active_, true); + nh_local_.param("publish_scan", p_publish_scan_, false); + nh_local_.param("publish_pcl", p_publish_pcl_, true); + + nh_local_.param("ranges_num", p_ranges_num_, 1000); + + nh_local_.param("min_scanner_range", p_min_scanner_range_, 0.05); + nh_local_.param("max_scanner_range", p_max_scanner_range_, 10.0); + + nh_local_.param("min_x_range", p_min_x_range_, -10.0); + nh_local_.param("max_x_range", p_max_x_range_, 10.0); + nh_local_.param("min_y_range", p_min_y_range_, -10.0); + nh_local_.param("max_y_range", p_max_y_range_, 10.0); + + nh_local_.param("fixed_frame_id", p_fixed_frame_id_, "map"); + nh_local_.param("target_frame_id", p_target_frame_id_, "robot"); + + if (p_active_ != prev_active) { + if (p_active_) { + front_scan_sub_ = nh_.subscribe("front_scan", 10, &ScansMerger::frontScanCallback, this); + rear_scan_sub_ = nh_.subscribe("rear_scan", 10, &ScansMerger::rearScanCallback, this); + scan_pub_ = nh_.advertise("scan", 10); + pcl_pub_ = nh_.advertise("pcl", 10); + } + else { + front_scan_sub_.shutdown(); + rear_scan_sub_.shutdown(); + scan_pub_.shutdown(); + pcl_pub_.shutdown(); + } + } + + return true; +} + +void ScansMerger::frontScanCallback(const sensor_msgs::LaserScan::ConstPtr front_scan) { + projector_.projectLaser(*front_scan, front_pcl_); + + front_scan_received_ = true; + front_scan_error_ = false; + + if (rear_scan_received_ || rear_scan_error_) + publishMessages(); + else + rear_scan_error_ = true; +} + +void ScansMerger::rearScanCallback(const sensor_msgs::LaserScan::ConstPtr rear_scan) { + projector_.projectLaser(*rear_scan, rear_pcl_); + + rear_scan_received_ = true; + rear_scan_error_ = false; + + if (front_scan_received_ || front_scan_error_) + publishMessages(); + else + front_scan_error_ = true; +} + +void ScansMerger::publishMessages() { + ros::Time now = ros::Time::now(); + + vector ranges; + vector points; + vector range_values; + sensor_msgs::ChannelFloat32 range_channel; + range_channel.name = "range"; + sensor_msgs::PointCloud new_front_pcl, new_rear_pcl; + + ranges.assign(p_ranges_num_, nanf("")); // Assign nan values + + if (!front_scan_error_) { + tf::StampedTransform target_to_lidar; + try { + tf_ls_.waitForTransform(p_target_frame_id_, now, front_pcl_.header.frame_id, front_pcl_.header.stamp, p_fixed_frame_id_, ros::Duration(0.05)); + tf_ls_.lookupTransform(p_target_frame_id_, now, front_pcl_.header.frame_id, front_pcl_.header.stamp, p_fixed_frame_id_, target_to_lidar); + tf_ls_.transformPointCloud(p_target_frame_id_, now, front_pcl_, p_fixed_frame_id_, new_front_pcl); + } + catch (tf::TransformException& ex) { + return; + } + const tf::Vector3 target_to_lidar_origin = target_to_lidar.getOrigin(); + ROS_INFO_STREAM_ONCE("Front lidar origin (frame " << front_pcl_.header.frame_id << ") is at (" << target_to_lidar_origin.getX() << ", " << target_to_lidar_origin.getY() << ") w.r.t. frame " << p_target_frame_id_); + + for (auto& point : new_front_pcl.points) { + const double range_x = point.x - target_to_lidar_origin.getX(); + const double range_y = point.y - target_to_lidar_origin.getY(); + const double range = sqrt(pow(range_x, 2.0) + pow(range_y, 2.0)); + + if (range_x > p_min_x_range_ && range_x < p_max_x_range_ && + range_y > p_min_y_range_ && range_y < p_max_y_range_ && + range > p_min_scanner_range_ && range < p_max_scanner_range_) { + if (p_publish_pcl_) { + points.push_back(point); + range_channel.values.push_back(range); + } + + if (p_publish_scan_) { + double angle = atan2(range_y, range_x); + + size_t idx = static_cast(p_ranges_num_ * (angle + M_PI) / (2.0 * M_PI)); + if (isnan(ranges[idx]) || range < ranges[idx]) + ranges[idx] = range; + } + } + } + } + + if (!rear_scan_error_) { + tf::StampedTransform target_to_lidar; + try { + tf_ls_.waitForTransform(p_target_frame_id_, now, rear_pcl_.header.frame_id, rear_pcl_.header.stamp, p_fixed_frame_id_, ros::Duration(0.05)); + tf_ls_.lookupTransform(p_target_frame_id_, now, rear_pcl_.header.frame_id, rear_pcl_.header.stamp, p_fixed_frame_id_, target_to_lidar); + tf_ls_.transformPointCloud(p_target_frame_id_, now, rear_pcl_, p_fixed_frame_id_, new_rear_pcl); + } + catch (tf::TransformException& ex) { + return; + } + const tf::Vector3 target_to_lidar_origin = target_to_lidar.getOrigin(); + ROS_INFO_STREAM_ONCE("Rear lidar origin (frame " << rear_pcl_.header.frame_id << ") is at (" << target_to_lidar_origin.getX() << ", " << target_to_lidar_origin.getY() << ") w.r.t. frame " << p_target_frame_id_); + + for (auto& point : new_rear_pcl.points) { + const double range_x = point.x - target_to_lidar_origin.getX(); + const double range_y = point.y - target_to_lidar_origin.getY(); + const double range = sqrt(pow(range_x, 2.0) + pow(range_y, 2.0)); + + if (range_x > p_min_x_range_ && range_x < p_max_x_range_ && + range_y > p_min_y_range_ && range_y < p_max_y_range_ && + range > p_min_scanner_range_ && range < p_max_scanner_range_) { + if (p_publish_pcl_) { + points.push_back(point); + range_channel.values.push_back(range); + } + + if (p_publish_scan_) { + double angle = atan2(range_y, range_x); + + size_t idx = static_cast(p_ranges_num_ * (angle + M_PI) / (2.0 * M_PI)); + if (isnan(ranges[idx]) || range < ranges[idx]) + ranges[idx] = range; + } + } + } + } + + if (p_publish_scan_) { + sensor_msgs::LaserScanPtr scan_msg(new sensor_msgs::LaserScan); + + scan_msg->header.frame_id = p_target_frame_id_; + scan_msg->header.stamp = now; + scan_msg->angle_min = -M_PI; + scan_msg->angle_max = M_PI; + scan_msg->angle_increment = 2.0 * M_PI / (p_ranges_num_ - 1); + scan_msg->time_increment = 0.0; + scan_msg->scan_time = 0.1; + scan_msg->range_min = p_min_scanner_range_; + scan_msg->range_max = p_max_scanner_range_; + scan_msg->ranges.assign(ranges.begin(), ranges.end()); + + scan_pub_.publish(scan_msg); + } + + if (p_publish_pcl_) { + sensor_msgs::PointCloudPtr pcl_msg(new sensor_msgs::PointCloud); + + pcl_msg->header.frame_id = p_target_frame_id_; + pcl_msg->header.stamp = now; + pcl_msg->points.assign(points.begin(), points.end()); + pcl_msg->channels.push_back(range_channel); + assert(range_channel.values.size() == points.size()); + + pcl_pub_.publish(pcl_msg); + } + + front_scan_received_ = false; + rear_scan_received_ = false; +} diff --git a/src/plugins_pkgs/gps_plugin/src/gps_plugin.cpp b/src/plugins_pkgs/gps_plugin/src/gps_plugin.cpp old mode 100644 new mode 100755 index d13a8d4..7d4f90e --- a/src/plugins_pkgs/gps_plugin/src/gps_plugin.cpp +++ b/src/plugins_pkgs/gps_plugin/src/gps_plugin.cpp @@ -27,8 +27,8 @@ namespace gazebo this->nodeHandle.reset(new ros::NodeHandle("localisationNODEvirt")); this->localisationPublisher = this->nodeHandle->advertise("/automobile/localisation", 10); - // Set up a ROS timer to call OnUpdate every second - this->timer = this->nodeHandle->createTimer(ros::Duration(0.01), &GPS::OnTimerEvent, this); + // Set up a ROS timer to call OnUpdate if you want more realistic increase the duration + this->timer = this->nodeHandle->createTimer(ros::Duration(0.00001), &GPS::OnTimerEvent, this); } void OnTimerEvent(const ros::TimerEvent&) diff --git a/src/pointcloud_to_laserscan/CHANGELOG.rst b/src/pointcloud_to_laserscan/CHANGELOG.rst new file mode 100644 index 0000000..2341c59 --- /dev/null +++ b/src/pointcloud_to_laserscan/CHANGELOG.rst @@ -0,0 +1,81 @@ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Changelog for package pointcloud_to_laserscan +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +1.4.1 (2019-08-30) +------------------ +* LaserScan to PointCloud node + nodelet (`#28 `_) +* fix roslint (`#29 `_) +* Merge pull request `#20 `_ from ros-perception/range_max_check + Add check for range_max +* Add check for range_max +* Contributors: Paul Bovbel, Rein Appeldoorn + +1.4.0 (2017-11-14) +------------------ +* Added inf_epsilon parameter that determines the value added to max range when use_infs parameter is set to false +* Merge pull request `#11 `_ from ros-perception/mikaelarguedas-patch-1 + update to use non deprecated pluginlib macro +* Migrate to package format 2; add roslint +* Add sane parameter defaults; fixup lint warnings +* update to use non deprecated pluginlib macro +* Contributors: Mikael Arguedas, Paul Bovbel, Prasanna Kannappan + +1.3.1 (2017-04-26) +------------------ +* Merge pull request `#4 `_ from yoshimalucky/fix-miscalculation-in-angle-increment + Fixed miscalculation in angle_increment in the launch files. +* fixed miscalculation in angle_increment in the launchfiles. +* Contributors: Paul Bovbel, yoshimalucky + +1.3.0 (2015-06-09) +------------------ +* Fix pointcloud to laserscan transform tolerance issues +* Move pointcloud_to_laserscan to new repository +* Contributors: Paul Bovbel + +1.2.7 (2015-06-08) +------------------ + +* Cleanup pointcloud_to_laserscan launch files +* Contributors: Paul Bovbel + +1.2.6 (2015-02-04) +------------------ +* Fix default value for concurrency +* Fix multithreaded lazy pub sub +* Contributors: Paul Bovbel + +1.2.5 (2015-01-20) +------------------ +* Switch to tf_sensor_msgs for transform +* Set parameters in sample launch files to default +* Add tolerance parameter +* Contributors: Paul Bovbel + +1.2.4 (2015-01-15) +------------------ +* Remove stray dependencies +* Refactor with tf2 and message filters +* Remove roslaunch check +* Fix regressions +* Refactor to allow debug messages from node and nodelet +* Contributors: Paul Bovbel + +1.2.3 (2015-01-10) +------------------ +* add launch tests +* refactor naming and fix nodelet export +* set default target frame to empty +* clean up package.xml +* Contributors: Paul Bovbel + +1.2.2 (2014-10-25) +------------------ +* clean up package.xml +* Fix header reference +* Fix flow +* Fix pointer assertion +* Finalize pointcloud to laserscan +* Initial pointcloud to laserscan commit +* Contributors: Paul Bovbel diff --git a/src/pointcloud_to_laserscan/CMakeLists.txt b/src/pointcloud_to_laserscan/CMakeLists.txt new file mode 100644 index 0000000..5d39d9d --- /dev/null +++ b/src/pointcloud_to_laserscan/CMakeLists.txt @@ -0,0 +1,62 @@ +cmake_minimum_required(VERSION 2.8.3) +project(pointcloud_to_laserscan) + +find_package(catkin REQUIRED COMPONENTS + laser_geometry + message_filters + nodelet + roscpp + sensor_msgs + tf2 + tf2_ros + tf2_sensor_msgs +) + +catkin_package( + INCLUDE_DIRS include + LIBRARIES laserscan_to_pointcloud pointcloud_to_laserscan + CATKIN_DEPENDS laser_geometry message_filters nodelet roscpp sensor_msgs tf2 tf2_ros tf2_sensor_msgs +) + +include_directories( + include + ${catkin_INCLUDE_DIRS} +) + +add_library(laserscan_to_pointcloud src/laserscan_to_pointcloud_nodelet.cpp) +target_link_libraries(laserscan_to_pointcloud ${catkin_LIBRARIES}) + +add_executable(laserscan_to_pointcloud_node src/laserscan_to_pointcloud_node.cpp) +target_link_libraries(laserscan_to_pointcloud_node laserscan_to_pointcloud ${catkin_LIBRARIES}) + +add_library(pointcloud_to_laserscan src/pointcloud_to_laserscan_nodelet.cpp) +target_link_libraries(pointcloud_to_laserscan ${catkin_LIBRARIES}) + +add_executable(pointcloud_to_laserscan_node src/pointcloud_to_laserscan_node.cpp) +target_link_libraries(pointcloud_to_laserscan_node pointcloud_to_laserscan ${catkin_LIBRARIES}) + +install(TARGETS + laserscan_to_pointcloud + laserscan_to_pointcloud_node + pointcloud_to_laserscan + pointcloud_to_laserscan_node + RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} + LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} + ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}) + +install(DIRECTORY include/${PROJECT_NAME}/ + DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION} +) +install(FILES nodelets.xml + DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} +) + +install(DIRECTORY launch + DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} +) + +if(CATKIN_ENABLE_TESTING) + find_package(roslint REQUIRED) + roslint_cpp() + roslint_add_test() +endif() diff --git a/src/pointcloud_to_laserscan/include/pointcloud_to_laserscan/laserscan_to_pointcloud_nodelet.h b/src/pointcloud_to_laserscan/include/pointcloud_to_laserscan/laserscan_to_pointcloud_nodelet.h new file mode 100644 index 0000000..60522e4 --- /dev/null +++ b/src/pointcloud_to_laserscan/include/pointcloud_to_laserscan/laserscan_to_pointcloud_nodelet.h @@ -0,0 +1,96 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2019, Eurotec, Netherlands + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Willow Garage, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * + */ + +/* + * Author: Rein Appeldoorn + */ + +#ifndef POINTCLOUD_TO_LASERSCAN_LASERSCAN_TO_POINTCLOUD_NODELET_H +#define POINTCLOUD_TO_LASERSCAN_LASERSCAN_TO_POINTCLOUD_NODELET_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace pointcloud_to_laserscan +{ +typedef tf2_ros::MessageFilter MessageFilter; + +//! \brief The PointCloudToLaserScanNodelet class to process incoming laserscans into pointclouds. +//! +class LaserScanToPointCloudNodelet : public nodelet::Nodelet +{ +public: + LaserScanToPointCloudNodelet(); + +private: + virtual void onInit(); + + void scanCallback(const sensor_msgs::LaserScanConstPtr& scan_msg); + void failureCallback(const sensor_msgs::LaserScanConstPtr& scan_msg, + tf2_ros::filter_failure_reasons::FilterFailureReason reason); + + void connectCb(); + void disconnectCb(); + + ros::NodeHandle nh_; + ros::NodeHandle private_nh_; + ros::Publisher pub_; + boost::mutex connect_mutex_; + + boost::shared_ptr tf2_; + boost::shared_ptr tf2_listener_; + message_filters::Subscriber sub_; + boost::shared_ptr message_filter_; + + laser_geometry::LaserProjection projector_; + + // ROS Parameters + unsigned int input_queue_size_; + std::string target_frame_; + double transform_tolerance_; +}; + +} // namespace pointcloud_to_laserscan + +#endif // POINTCLOUD_TO_LASERSCAN_LASERSCAN_TO_POINTCLOUD_NODELET_H diff --git a/src/pointcloud_to_laserscan/include/pointcloud_to_laserscan/pointcloud_to_laserscan_nodelet.h b/src/pointcloud_to_laserscan/include/pointcloud_to_laserscan/pointcloud_to_laserscan_nodelet.h new file mode 100644 index 0000000..930ceba --- /dev/null +++ b/src/pointcloud_to_laserscan/include/pointcloud_to_laserscan/pointcloud_to_laserscan_nodelet.h @@ -0,0 +1,97 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2010-2012, Willow Garage, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Willow Garage, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * + */ + +/* + * Author: Paul Bovbel + */ + +#ifndef POINTCLOUD_TO_LASERSCAN_POINTCLOUD_TO_LASERSCAN_NODELET_H +#define POINTCLOUD_TO_LASERSCAN_POINTCLOUD_TO_LASERSCAN_NODELET_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace pointcloud_to_laserscan +{ +typedef tf2_ros::MessageFilter MessageFilter; +/** +* Class to process incoming pointclouds into laserscans. Some initial code was pulled from the defunct turtlebot +* pointcloud_to_laserscan implementation. +*/ +class PointCloudToLaserScanNodelet : public nodelet::Nodelet +{ +public: + PointCloudToLaserScanNodelet(); + +private: + virtual void onInit(); + + void cloudCb(const sensor_msgs::PointCloud2ConstPtr& cloud_msg); + void failureCb(const sensor_msgs::PointCloud2ConstPtr& cloud_msg, + tf2_ros::filter_failure_reasons::FilterFailureReason reason); + + void connectCb(); + + void disconnectCb(); + + ros::NodeHandle nh_, private_nh_; + ros::Publisher pub_; + boost::mutex connect_mutex_; + + boost::shared_ptr tf2_; + boost::shared_ptr tf2_listener_; + message_filters::Subscriber sub_; + boost::shared_ptr message_filter_; + + // ROS Parameters + unsigned int input_queue_size_; + std::string target_frame_; + double tolerance_; + double min_height_, max_height_, angle_min_, angle_max_, angle_increment_, scan_time_, range_min_, range_max_; + bool use_inf_; + double inf_epsilon_; +}; + +} // namespace pointcloud_to_laserscan + +#endif // POINTCLOUD_TO_LASERSCAN_POINTCLOUD_TO_LASERSCAN_NODELET_H diff --git a/src/pointcloud_to_laserscan/launch/sample_laserscan_to_pointcloud_node.launch b/src/pointcloud_to_laserscan/launch/sample_laserscan_to_pointcloud_node.launch new file mode 100644 index 0000000..594be32 --- /dev/null +++ b/src/pointcloud_to_laserscan/launch/sample_laserscan_to_pointcloud_node.launch @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + target_frame: scan # Leave disabled to output pointcloud in scan frame + transform_tolerance: 0.01 + + # Concurrency level, affects number of pointclouds queued for processing and number of threads used + # 0 : Detect number of cores + # 1 : Single threaded + # 2->inf : Parallelism level + concurrency_level: 1 + + + + + diff --git a/src/pointcloud_to_laserscan/launch/sample_laserscan_to_pointcloud_nodelet.launch b/src/pointcloud_to_laserscan/launch/sample_laserscan_to_pointcloud_nodelet.launch new file mode 100644 index 0000000..eca260b --- /dev/null +++ b/src/pointcloud_to_laserscan/launch/sample_laserscan_to_pointcloud_nodelet.launch @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + target_frame: scan # Leave disabled to output pointcloud in scan frame + transform_tolerance: 0.01 + + # Concurrency level, affects number of pointclouds queued for processing and number of threads used + # 0 : Detect number of cores + # 1 : Single threaded + # 2->inf : Parallelism level + concurrency_level: 1 + + + + + diff --git a/src/pointcloud_to_laserscan/launch/sample_node.launch b/src/pointcloud_to_laserscan/launch/sample_node.launch new file mode 100644 index 0000000..78ffa4d --- /dev/null +++ b/src/pointcloud_to_laserscan/launch/sample_node.launch @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + target_frame: camera_link # Leave disabled to output scan in pointcloud frame + transform_tolerance: 0.01 + min_height: 0.0 + max_height: 1.0 + + angle_min: -1.5708 # -M_PI/2 + angle_max: 1.5708 # M_PI/2 + angle_increment: 0.0087 # M_PI/360.0 + scan_time: 0.3333 + range_min: 0.45 + range_max: 4.0 + use_inf: true + inf_epsilon: 1.0 + + # Concurrency level, affects number of pointclouds queued for processing and number of threads used + # 0 : Detect number of cores + # 1 : Single threaded + # 2->inf : Parallelism level + concurrency_level: 1 + + + + + diff --git a/src/pointcloud_to_laserscan/launch/sample_nodelet.launch b/src/pointcloud_to_laserscan/launch/sample_nodelet.launch new file mode 100644 index 0000000..ecf5e74 --- /dev/null +++ b/src/pointcloud_to_laserscan/launch/sample_nodelet.launch @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + target_frame: camera_link # Leave disabled to output scan in pointcloud frame + transform_tolerance: 0.01 + min_height: 0.0 + max_height: 1.0 + + angle_min: -1.5708 # -M_PI/2 + angle_max: 1.5708 # M_PI/2 + angle_increment: 0.0087 # M_PI/360.0 + scan_time: 0.3333 + range_min: 0.45 + range_max: 4.0 + use_inf: true + inf_epsilon: 1.0 + + # Concurrency level, affects number of pointclouds queued for processing, thread number governed by nodelet manager + # 0 : Detect number of cores + # 1 : Single threaded + # 2->inf : Parallelism level + concurrency_level: 1 + + + + + diff --git a/src/pointcloud_to_laserscan/nodelets.xml b/src/pointcloud_to_laserscan/nodelets.xml new file mode 100644 index 0000000..7621c46 --- /dev/null +++ b/src/pointcloud_to_laserscan/nodelets.xml @@ -0,0 +1,23 @@ + + + + + Nodelet to convert sensor_msgs/PointCloud2 to sensor_msgs/LaserScans. + + + + + + + + + + Nodelet to convert sensor_msgs/LaserScan to sensor_msgs/PointCloud2. + + + + diff --git a/src/pointcloud_to_laserscan/package.xml b/src/pointcloud_to_laserscan/package.xml new file mode 100644 index 0000000..7da0f3a --- /dev/null +++ b/src/pointcloud_to_laserscan/package.xml @@ -0,0 +1,34 @@ + + + pointcloud_to_laserscan + 1.4.1 + Converts a 3D Point Cloud into a 2D laser scan. This is useful for making devices like the Kinect appear like a laser scanner for 2D-based algorithms (e.g. laser-based SLAM). + + Paul Bovbel + Michel Hidalgo + Paul Bovbel + Tully Foote + + BSD + + http://ros.org/wiki/perception_pcl + https://github.com/ros-perception/perception_pcl/issues + https://github.com/ros-perception/perception_pcl + + catkin + + laser_geometry + message_filters + nodelet + roscpp + sensor_msgs + tf2 + tf2_ros + tf2_sensor_msgs + + roslint + + + + + diff --git a/src/pointcloud_to_laserscan/src/laserscan_to_pointcloud_node.cpp b/src/pointcloud_to_laserscan/src/laserscan_to_pointcloud_node.cpp new file mode 100644 index 0000000..70c34a8 --- /dev/null +++ b/src/pointcloud_to_laserscan/src/laserscan_to_pointcloud_node.cpp @@ -0,0 +1,68 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2019, Eurotec, Netherlands + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Willow Garage, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * + */ + +/* + * Author: Rein Appeldoorn + */ + +#include +#include +#include + +int main(int argc, char** argv) +{ + ros::init(argc, argv, "laserscan_to_pointcloud_node"); + ros::NodeHandle private_nh("~"); + int concurrency_level = private_nh.param("concurrency_level", 0); + + nodelet::Loader nodelet; + nodelet::M_string remap(ros::names::getRemappings()); + nodelet::V_string nargv; + std::string nodelet_name = ros::this_node::getName(); + nodelet.load(nodelet_name, "pointcloud_to_laserscan/laserscan_to_pointcloud_nodelet", remap, nargv); + + boost::shared_ptr spinner; + if (concurrency_level) + { + spinner.reset(new ros::MultiThreadedSpinner(static_cast(concurrency_level))); + } + else + { + spinner.reset(new ros::MultiThreadedSpinner()); + } + spinner->spin(); + return 0; +} diff --git a/src/pointcloud_to_laserscan/src/laserscan_to_pointcloud_nodelet.cpp b/src/pointcloud_to_laserscan/src/laserscan_to_pointcloud_nodelet.cpp new file mode 100644 index 0000000..4f2adc7 --- /dev/null +++ b/src/pointcloud_to_laserscan/src/laserscan_to_pointcloud_nodelet.cpp @@ -0,0 +1,155 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2019, Eurotec, Netherlands + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Willow Garage, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * + */ + +/* + * Author: Rein Appeldoorn + */ + +#include +#include +#include +#include +#include +#include + +namespace pointcloud_to_laserscan +{ +LaserScanToPointCloudNodelet::LaserScanToPointCloudNodelet() +{ +} + +void LaserScanToPointCloudNodelet::onInit() +{ + boost::mutex::scoped_lock lock(connect_mutex_); + private_nh_ = getPrivateNodeHandle(); + + private_nh_.param("target_frame", target_frame_, ""); + private_nh_.param("transform_tolerance", transform_tolerance_, 0.01); + + int concurrency_level = private_nh_.param("concurrency_level", concurrency_level); + + // Check if explicitly single threaded, otherwise, let nodelet manager dictate thread pool size + if (concurrency_level == 1) + { + nh_ = getNodeHandle(); + } + else + { + nh_ = getMTNodeHandle(); + } + + // Only queue one pointcloud per running thread + if (concurrency_level > 0) + { + input_queue_size_ = static_cast(concurrency_level); + } + else + { + input_queue_size_ = boost::thread::hardware_concurrency(); + } + + // if pointcloud target frame specified, we need to filter by transform availability + if (!target_frame_.empty()) + { + tf2_.reset(new tf2_ros::Buffer()); + tf2_listener_.reset(new tf2_ros::TransformListener(*tf2_)); + message_filter_.reset(new MessageFilter(sub_, *tf2_, target_frame_, input_queue_size_, nh_)); + message_filter_->registerCallback(boost::bind(&LaserScanToPointCloudNodelet::scanCallback, this, _1)); + message_filter_->registerFailureCallback(boost::bind(&LaserScanToPointCloudNodelet::failureCallback, this, _1, _2)); + } + else // otherwise setup direct subscription + { + sub_.registerCallback(boost::bind(&LaserScanToPointCloudNodelet::scanCallback, this, _1)); + } + + pub_ = + nh_.advertise("cloud", 10, boost::bind(&LaserScanToPointCloudNodelet::connectCb, this), + boost::bind(&LaserScanToPointCloudNodelet::disconnectCb, this)); +} + +void LaserScanToPointCloudNodelet::connectCb() +{ + boost::mutex::scoped_lock lock(connect_mutex_); + if (pub_.getNumSubscribers() > 0 && sub_.getSubscriber().getNumPublishers() == 0) + { + NODELET_INFO("Got a subscriber to cloud, starting laserscan subscriber"); + sub_.subscribe(nh_, "scan_in", input_queue_size_); + } +} + +void LaserScanToPointCloudNodelet::disconnectCb() +{ + boost::mutex::scoped_lock lock(connect_mutex_); + if (pub_.getNumSubscribers() == 0) + { + NODELET_INFO("No subscibers to cloud, shutting down subscriber to laserscan"); + sub_.unsubscribe(); + } +} + +void LaserScanToPointCloudNodelet::failureCallback(const sensor_msgs::LaserScanConstPtr& scan_msg, + tf2_ros::filter_failure_reasons::FilterFailureReason reason) +{ + NODELET_WARN_STREAM_THROTTLE(1.0, "Can't transform laserscan from frame " << scan_msg->header.frame_id << " to " + << message_filter_->getTargetFramesString() + << " at time " << scan_msg->header.stamp + << ", reason: " << reason); +} + +void LaserScanToPointCloudNodelet::scanCallback(const sensor_msgs::LaserScanConstPtr& scan_msg) +{ + sensor_msgs::PointCloud2Ptr scan_cloud; + scan_cloud.reset(new sensor_msgs::PointCloud2); + projector_.projectLaser(*scan_msg, *scan_cloud); + + // Transform cloud if necessary + if (!target_frame_.empty() && scan_cloud->header.frame_id != target_frame_) + { + try + { + *scan_cloud = tf2_->transform(*scan_cloud, target_frame_); + } + catch (tf2::TransformException& ex) + { + NODELET_ERROR_STREAM("Transform failure: " << ex.what()); + return; + } + } + pub_.publish(*scan_cloud); +} +} // namespace pointcloud_to_laserscan + +PLUGINLIB_EXPORT_CLASS(pointcloud_to_laserscan::LaserScanToPointCloudNodelet, nodelet::Nodelet) diff --git a/src/pointcloud_to_laserscan/src/pointcloud_to_laserscan_node.cpp b/src/pointcloud_to_laserscan/src/pointcloud_to_laserscan_node.cpp new file mode 100644 index 0000000..013b064 --- /dev/null +++ b/src/pointcloud_to_laserscan/src/pointcloud_to_laserscan_node.cpp @@ -0,0 +1,69 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2010-2012, Willow Garage, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Willow Garage, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * + */ + +/* + * Author: Paul Bovbel + */ + +#include +#include +#include + +int main(int argc, char** argv) +{ + ros::init(argc, argv, "pointcloud_to_laserscan_node"); + ros::NodeHandle private_nh("~"); + int concurrency_level; + private_nh.param("concurrency_level", concurrency_level, 0); + + nodelet::Loader nodelet; + nodelet::M_string remap(ros::names::getRemappings()); + nodelet::V_string nargv; + std::string nodelet_name = ros::this_node::getName(); + nodelet.load(nodelet_name, "pointcloud_to_laserscan/pointcloud_to_laserscan_nodelet", remap, nargv); + + boost::shared_ptr spinner; + if (concurrency_level) + { + spinner.reset(new ros::MultiThreadedSpinner(concurrency_level)); + } + else + { + spinner.reset(new ros::MultiThreadedSpinner()); + } + spinner->spin(); + return 0; +} diff --git a/src/pointcloud_to_laserscan/src/pointcloud_to_laserscan_nodelet.cpp b/src/pointcloud_to_laserscan/src/pointcloud_to_laserscan_nodelet.cpp new file mode 100644 index 0000000..184ff7e --- /dev/null +++ b/src/pointcloud_to_laserscan/src/pointcloud_to_laserscan_nodelet.cpp @@ -0,0 +1,247 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2010-2012, Willow Garage, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Willow Garage, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * + */ + +/* + * Author: Paul Bovbel + */ + +#include +#include +#include +#include +#include +#include +#include + +namespace pointcloud_to_laserscan +{ +PointCloudToLaserScanNodelet::PointCloudToLaserScanNodelet() +{ +} + +void PointCloudToLaserScanNodelet::onInit() +{ + boost::mutex::scoped_lock lock(connect_mutex_); + private_nh_ = getPrivateNodeHandle(); + + private_nh_.param("target_frame", target_frame_, ""); + private_nh_.param("transform_tolerance", tolerance_, 0.01); + private_nh_.param("min_height", min_height_, std::numeric_limits::min()); + private_nh_.param("max_height", max_height_, std::numeric_limits::max()); + + private_nh_.param("angle_min", angle_min_, -M_PI); + private_nh_.param("angle_max", angle_max_, M_PI); + private_nh_.param("angle_increment", angle_increment_, M_PI / 180.0); + private_nh_.param("scan_time", scan_time_, 1.0 / 30.0); + private_nh_.param("range_min", range_min_, 0.0); + private_nh_.param("range_max", range_max_, std::numeric_limits::max()); + private_nh_.param("inf_epsilon", inf_epsilon_, 1.0); + + int concurrency_level; + private_nh_.param("concurrency_level", concurrency_level, 1); + private_nh_.param("use_inf", use_inf_, true); + + // Check if explicitly single threaded, otherwise, let nodelet manager dictate thread pool size + if (concurrency_level == 1) + { + nh_ = getNodeHandle(); + } + else + { + nh_ = getMTNodeHandle(); + } + + // Only queue one pointcloud per running thread + if (concurrency_level > 0) + { + input_queue_size_ = concurrency_level; + } + else + { + input_queue_size_ = boost::thread::hardware_concurrency(); + } + + // if pointcloud target frame specified, we need to filter by transform availability + if (!target_frame_.empty()) + { + tf2_.reset(new tf2_ros::Buffer()); + tf2_listener_.reset(new tf2_ros::TransformListener(*tf2_)); + message_filter_.reset(new MessageFilter(sub_, *tf2_, target_frame_, input_queue_size_, nh_)); + message_filter_->registerCallback(boost::bind(&PointCloudToLaserScanNodelet::cloudCb, this, _1)); + message_filter_->registerFailureCallback(boost::bind(&PointCloudToLaserScanNodelet::failureCb, this, _1, _2)); + } + else // otherwise setup direct subscription + { + sub_.registerCallback(boost::bind(&PointCloudToLaserScanNodelet::cloudCb, this, _1)); + } + + pub_ = nh_.advertise("scan", 10, boost::bind(&PointCloudToLaserScanNodelet::connectCb, this), + boost::bind(&PointCloudToLaserScanNodelet::disconnectCb, this)); +} + +void PointCloudToLaserScanNodelet::connectCb() +{ + boost::mutex::scoped_lock lock(connect_mutex_); + if (pub_.getNumSubscribers() > 0 && sub_.getSubscriber().getNumPublishers() == 0) + { + NODELET_INFO("Got a subscriber to scan, starting subscriber to pointcloud"); + sub_.subscribe(nh_, "cloud_in", input_queue_size_); + } +} + +void PointCloudToLaserScanNodelet::disconnectCb() +{ + boost::mutex::scoped_lock lock(connect_mutex_); + if (pub_.getNumSubscribers() == 0) + { + NODELET_INFO("No subscibers to scan, shutting down subscriber to pointcloud"); + sub_.unsubscribe(); + } +} + +void PointCloudToLaserScanNodelet::failureCb(const sensor_msgs::PointCloud2ConstPtr& cloud_msg, + tf2_ros::filter_failure_reasons::FilterFailureReason reason) +{ + NODELET_WARN_STREAM_THROTTLE(1.0, "Can't transform pointcloud from frame " << cloud_msg->header.frame_id << " to " + << message_filter_->getTargetFramesString() + << " at time " << cloud_msg->header.stamp + << ", reason: " << reason); +} + +void PointCloudToLaserScanNodelet::cloudCb(const sensor_msgs::PointCloud2ConstPtr& cloud_msg) +{ + // build laserscan output + sensor_msgs::LaserScan output; + output.header = cloud_msg->header; + if (!target_frame_.empty()) + { + output.header.frame_id = target_frame_; + } + + output.angle_min = angle_min_; + output.angle_max = angle_max_; + output.angle_increment = angle_increment_; + output.time_increment = 0.0; + output.scan_time = scan_time_; + output.range_min = range_min_; + output.range_max = range_max_; + + // determine amount of rays to create + uint32_t ranges_size = std::ceil((output.angle_max - output.angle_min) / output.angle_increment); + + // determine if laserscan rays with no obstacle data will evaluate to infinity or max_range + if (use_inf_) + { + output.ranges.assign(ranges_size, std::numeric_limits::infinity()); + } + else + { + output.ranges.assign(ranges_size, output.range_max + inf_epsilon_); + } + + sensor_msgs::PointCloud2ConstPtr cloud_out; + sensor_msgs::PointCloud2Ptr cloud; + + // Transform cloud if necessary + if (!(output.header.frame_id == cloud_msg->header.frame_id)) + { + try + { + cloud.reset(new sensor_msgs::PointCloud2); + tf2_->transform(*cloud_msg, *cloud, target_frame_, ros::Duration(tolerance_)); + cloud_out = cloud; + } + catch (tf2::TransformException& ex) + { + NODELET_ERROR_STREAM("Transform failure: " << ex.what()); + return; + } + } + else + { + cloud_out = cloud_msg; + } + + // Iterate through pointcloud + for (sensor_msgs::PointCloud2ConstIterator iter_x(*cloud_out, "x"), iter_y(*cloud_out, "y"), + iter_z(*cloud_out, "z"); + iter_x != iter_x.end(); ++iter_x, ++iter_y, ++iter_z) + { + if (std::isnan(*iter_x) || std::isnan(*iter_y) || std::isnan(*iter_z)) + { + NODELET_DEBUG("rejected for nan in point(%f, %f, %f)\n", *iter_x, *iter_y, *iter_z); + continue; + } + + if (*iter_z > max_height_ || *iter_z < min_height_) + { + NODELET_DEBUG("rejected for height %f not in range (%f, %f)\n", *iter_z, min_height_, max_height_); + continue; + } + + double range = hypot(*iter_x, *iter_y); + if (range < range_min_) + { + NODELET_DEBUG("rejected for range %f below minimum value %f. Point: (%f, %f, %f)", range, range_min_, *iter_x, + *iter_y, *iter_z); + continue; + } + if (range > range_max_) + { + NODELET_DEBUG("rejected for range %f above maximum value %f. Point: (%f, %f, %f)", range, range_max_, *iter_x, + *iter_y, *iter_z); + continue; + } + + double angle = atan2(*iter_y, *iter_x); + if (angle < output.angle_min || angle > output.angle_max) + { + NODELET_DEBUG("rejected for angle %f not in range (%f, %f)\n", angle, output.angle_min, output.angle_max); + continue; + } + + // overwrite range at laserscan ray if new range is smaller + int index = (angle - output.angle_min) / output.angle_increment; + if (range < output.ranges[index]) + { + output.ranges[index] = range; + } + } + pub_.publish(output); +} +} // namespace pointcloud_to_laserscan + +PLUGINLIB_EXPORT_CLASS(pointcloud_to_laserscan::PointCloudToLaserScanNodelet, nodelet::Nodelet) diff --git a/src/sim_pkg/config/ekf_localization.yaml b/src/sim_pkg/config/ekf_localization.yaml new file mode 100644 index 0000000..845a147 --- /dev/null +++ b/src/sim_pkg/config/ekf_localization.yaml @@ -0,0 +1,270 @@ +# The frequency, in Hz, at which the filter will output a position estimate. Note that the filter will not begin +# computation until it receives at least one message from one of the inputs. It will then run continuously at the +# frequency specified here, regardless of whether it receives more measurements. Defaults to 30 if unspecified. +frequency: 30 + +# The period, in seconds, after which we consider a sensor to have timed out. In this event, we carry out a predict +# cycle on the EKF without correcting it. This parameter can be thought of as the minimum frequency with which the +# filter will generate new output. Defaults to 1 / frequency if not specified. +sensor_timeout: 0.5 + +# ekf_localization_node and ukf_localization_node both use a 3D omnidirectional motion model. If this parameter is +# set to true, no 3D information will be used in your state estimate. Use this if you are operating in a planar +# environment and want to ignore the effect of small variations in the ground plane that might otherwise be detected +# by, for example, an IMU. Defaults to false if unspecified. +two_d_mode: false + +use_sim_time: true +# Use this parameter to provide an offset to the transform generated by ekf_localization_node. This can be used for +# future dating the transform, which is required for interaction with some other packages. Defaults to 0.0 if +# unspecified. +transform_time_offset: 0.0 + +# Use this parameter to provide specify how long the tf listener should wait for a transform to become available. +# Defaults to 0.0 if unspecified. +transform_timeout: 1.0 + +# Debug settings. Not for the faint of heart. Outputs a ludicrous amount of information to the file specified by +# debug_out_file. I hope you like matrices! Please note that setting this to true will have strongly deleterious +# effects on the performance of the node. Defaults to false if unspecified. +debug: false + +# Defaults to "robot_localization_debug.txt" if unspecified. Please specify the full path. +debug_out_file: /home/mekatronom/out/robot_localization_debug.txt + +# Whether to broadcast the transformation over the /tf topic. Defaults to true if unspecified. +publish_tf: true + +# Whether to publish the acceleration state. Defaults to false if unspecified. +publish_acceleration: false + +# REP-105 (http://www.ros.org/reps/rep-0105.html) specifies four principal coordinate frames: base_link, odom, map, and +# earth. base_link is the coordinate frame that is affixed to the robot. Both odom and map are world-fixed frames. +# The robot's position in the odom frame will drift over time, but is accurate in the short term and should be +# continuous. The odom frame is therefore the best frame for executing local motion plans. The map frame, like the odom +# frame, is a world-fixed coordinate frame, and while it contains the most globally accurate position estimate for your +# robot, it is subject to discrete jumps, e.g., due to the fusion of GPS data or a correction from a map-based +# localization node. The earth frame is used to relate multiple map frames by giving them a common reference frame. +# ekf_localization_node and ukf_localization_node are not concerned with the earth frame. +# Here is how to use the following settings: +# 1. Set the map_frame, odom_frame, and base_link frames to the appropriate frame names for your system. +# 1a. If your system does not have a map_frame, just remove it, and make sure "world_frame" is set to the value of +# odom_frame. +# 2. If you are fusing continuous position data such as wheel encoder odometry, visual odometry, or IMU data, set +# "world_frame" to your odom_frame value. This is the default behavior for robot_localization's state estimation nodes. +# 3. If you are fusing global absolute position data that is subject to discrete jumps (e.g., GPS or position updates +# from landmark observations) then: +# 3a. Set your "world_frame" to your map_frame value +# 3b. MAKE SURE something else is generating the odom->base_link transform. Note that this can even be another state +# estimation node from robot_localization! However, that instance should *not* fuse the global data. +map_frame: map # Defaults to "map" if unspecified +odom_frame: odom # Defaults to "odom" if unspecified +base_link_frame: automobile_chassis_link # Defaults to "base_link" if unspecified +world_frame: odom # Defaults to the value of odom_frame if unspecified + +######################### BURADAKI ACIKLAMA ONEMLI ######################### +# The filter accepts an arbitrary number of inputs from each input message type (nav_msgs/Odometry, +# geometry_msgs/PoseWithCovarianceStamped, geometry_msgs/TwistWithCovarianceStamped, +# sensor_msgs/Imu). To add an input, simply append the next number in the sequence to its "base" name, e.g., odom0, +# odom1, twist0, twist1, imu0, imu1, imu2, etc. The value should be the topic name. These parameters obviously have no +# default values, and must be specified. + +# odom0: example/odom + +# # Each sensor reading updates some or all of the filter's state. These options give you greater control over which +# # values from each measurement are fed to the filter. For example, if you have an odometry message as input, but only +# # want to use its Z position value, then set the entire vector to false, except for the third entry. The order of the +# # values is x, y, z, roll, pitch, yaw, vx, vy, vz, vroll, vpitch, vyaw, ax, ay, az. Note that not some message types +# # do not provide some of the state variables estimated by the filter. For example, a TwistWithCovarianceStamped message +# # has no pose information, so the first six values would be meaningless in that case. Each vector defaults to all false +# # if unspecified, effectively making this parameter required for each sensor. +# odom0_config: [true, true, false, +# false, false, false, +# false, false, false, +# false, false, true, +# false, false, false] + +# # If you have high-frequency data or are running with a low frequency parameter value, then you may want to increase +# # the size of the subscription queue so that more measurements are fused. +# odom0_queue_size: 2 + +# # [ADVANCED] Large messages in ROS can exhibit strange behavior when they arrive at a high frequency. This is a result +# # of Nagle's algorithm. This option tells the ROS subscriber to use the tcpNoDelay option, which disables Nagle's +# # algorithm. +# odom0_nodelay: false + +# # [ADVANCED] When measuring one pose variable with two sensors, a situation can arise in which both sensors under- +# # report their covariances. This can lead to the filter rapidly jumping back and forth between each measurement as they +# # arrive. In these cases, it often makes sense to (a) correct the measurement covariances, or (b) if velocity is also +# # measured by one of the sensors, let one sensor measure pose, and the other velocity. However, doing (a) or (b) isn't +# # always feasible, and so we expose the differential parameter. When differential mode is enabled, all absolute pose +# # data is converted to velocity data by differentiating the absolute pose measurements. These velocities are then +# # integrated as usual. NOTE: this only applies to sensors that provide pose measurements; setting differential to true +# # for twist measurements has no effect. +# odom0_differential: false + +# # [ADVANCED] When the node starts, if this parameter is true, then the first measurement is treated as a "zero point" +# # for all future measurements. While you can achieve the same effect with the differential paremeter, the key +# # difference is that the relative parameter doesn't cause the measurement to be converted to a velocity before +# # integrating it. If you simply want your measurements to start at 0 for a given sensor, set this to true. +# odom0_relative: false + +# [ADVANCED] If your data is subject to outliers, use these threshold settings, expressed as Mahalanobis distances, to +# control how far away from the current vehicle state a sensor measurement is permitted to be. Each defaults to +# numeric_limits::max() if unspecified. It is strongly recommended that these parameters be removed if not +# required. Data is specified at the level of pose and twist variables, rather than for each variable in isolation. +# For messages that have both pose and twist data, the parameter specifies to which part of the message we are applying +# the thresholds. + +# odom0_pose_rejection_threshold: 5 +# odom0_twist_rejection_threshold: 1 + +# # Further input parameter examples +# odom1: example/another_odom +# odom1_config: [false, false, true, +# false, false, false, +# false, false, false, +# false, false, true, +# false, false, false] +# odom1_differential: false +# odom1_relative: true +# odom1_queue_size: 2 +# odom1_pose_rejection_threshold: 2 +# odom1_twist_rejection_threshold: 0.2 +# odom1_nodelay: false + +pose0: /automobile/localisation +pose0_config: [true, true, false, + false, false, false, + false, false, false, + false, false, false, + false, false, false] +pose0_queue_size: 10 +pose0_nodelay: false +pose0_differential: false +pose0_relative: false + +# pose0_covariance: [0.04, 0, 0, 0, 0, 0, 0, +# 0, 0.04, 0, 0, 0, 0, 0, 0, +# 0, 0, 0, 0, 0, 0, 0, 0, 0, +# 0, 0, 0, 0, 0, 0, 0, 0, 0, +# 0, 0, 0, 0, 0, 0, 0, 0, 0] + +# twist0: example/twist +# twist0_config: [false, false, false, +# false, false, false, +# true, true, true, +# false, false, false, +# false, false, false] +# twist0_queue_size: 3 +# twist0_rejection_threshold: 2 +# twist0_nodelay: false + +imu0: "/automobile/IMU" +imu0_config: [false, false, false, # x, y, z + false, false, true, # roll, pitch, yaw + false, false, false, # vx, vy, vz + false, false, true, # vroll, vpitch, vyaw + false, false, false] # ax, ay, az +imu0_queue_size: 30 +imu0_nodelay: false +imu0_differential: false +imu0_relative: true + +# imu0_orientation_covariance: [0.09, 0, 0, +# 0, 0.09, 0, +# 0, 0, 0.09] + +# imu0_angular_velocity_covariance: [0.09, 0, 0, +# 0, 0.09, 0, +# 0, 0, 0.09] + +# imu0_linear_acceleration_covariance: [0.09, 0, 0, +# 0, 0.09, 0, +# 0, 0, 1.0] + + +# [ADVANCED] Some IMUs automatically remove acceleration due to gravity, and others don't. If yours doesn't, please set +# this to true, and *make sure* your data conforms to REP-103, specifically, that the data is in ENU frame. +imu0_remove_gravitational_acceleration: false +# imu0_pose_rejection_threshold: 0.2 # Note the difference in parameter names +# # imu0_twist_rejection_threshold: 0.2 # +# imu0_linear_acceleration_rejection_threshold: 0.2 # + + + +# [ADVANCED] The EKF and UKF models follow a standard predict/correct cycle. During prediction, if there is no +# acceleration reference, the velocity at time t+1 is simply predicted to be the same as the velocity at time t. During +# correction, this predicted value is fused with the measured value to produce the new velocity estimate. This can be +# problematic, as the final velocity will effectively be a weighted average of the old velocity and the new one. When +# this velocity is the integrated into a new pose, the result can be sluggish covergence. This effect is especially +# noticeable with LIDAR data during rotations. To get around it, users can try inflating the process_noise_covariance +# for the velocity variable in question, or decrease the variance of the variable in question in the measurement +# itself. In addition, users can also take advantage of the control command being issued to the robot at the time we +# make the prediction. If control is used, it will get converted into an acceleration term, which will be used during +# predicition. Note that if an acceleration measurement for the variable in question is available from one of the +# inputs, the control term will be ignored. +# # Whether or not we use the control input during predicition. Defaults to false. +# use_control: false +# # Whether the input (assumed to be cmd_vel) is a geometry_msgs/Twist or geometry_msgs/TwistStamped message. Defaults to +# # false. +# stamped_control: false +# # The last issued control command will be used in prediction for this period. Defaults to 0.2. +# control_timeout: 0.2 +# # Which velocities are being controlled. Order is vx, vy, vz, vroll, vpitch, vyaw. +# control_config: [true, false, false, false, false, true] +# # Places limits on how large the acceleration term will be. Should match your robot's kinematics. +# acceleration_limits: [1.3, 0.0, 0.0, 0.0, 0.0, 3.4] +# # Acceleration and deceleration limits are not always the same for robots. +# deceleration_limits: [1.3, 0.0, 0.0, 0.0, 0.0, 4.5] +# # If your robot cannot instantaneously reach its acceleration limit, the permitted change can be controlled with these +# # gains +# acceleration_gains: [0.8, 0.0, 0.0, 0.0, 0.0, 0.9] +# # If your robot cannot instantaneously reach its deceleration limit, the permitted change can be controlled with these +# # gains +# deceleration_gains: [1.0, 0.0, 0.0, 0.0, 0.0, 1.0] + +# [ADVANCED] The process noise covariance matrix can be difficult to tune, and can vary for each application, so it is +# exposed as a configuration parameter. This matrix represents the noise we add to the total error after each +# prediction step. The better the omnidirectional motion model matches your system, the smaller these values can be. +# However, if users find that a given variable is slow to converge, one approach is to increase the +# process_noise_covariance diagonal value for the variable in question, which will cause the filter's predicted error +# to be larger, which will cause the filter to trust the incoming measurement more during correction. The values are +# ordered as x, y, z, roll, pitch, yaw, vx, vy, vz, vroll, vpitch, vyaw, ax, ay, az. Defaults to the matrix below if +# unspecified. +process_noise_covariance: [0.09, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0.09, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0.06, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0.03, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0.03, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0.06, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0.025, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0.025, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0.04, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.01, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.01, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.02, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.01, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.01, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.015] + +# [ADVANCED] This represents the initial value for the state estimate error covariance matrix. Setting a diagonal +# value (variance) to a large value will result in rapid convergence for initial measurements of the variable in +# question. Users should take care not to use large values for variables that will not be measured directly. The values +# are ordered as x, y, z, roll, pitch, yaw, vx, vy, vz, vroll, vpitch, vyaw, ax, ay, az. Defaults to the matrix below +#if unspecified. +initial_estimate_covariance: [1e-9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1e-9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1e-9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1e-9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1e-9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1e-9, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1e-9, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1e-9, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1e-9, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1e-9, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1e-9, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1e-9, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1e-9, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1e-9, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1e-9] diff --git a/src/sim_pkg/launch/bash_scripts/launch_obstacle_detector.sh b/src/sim_pkg/launch/bash_scripts/launch_obstacle_detector.sh new file mode 100755 index 0000000..975f4c9 --- /dev/null +++ b/src/sim_pkg/launch/bash_scripts/launch_obstacle_detector.sh @@ -0,0 +1,2 @@ +#!/bin/bash +roslaunch obstacle_detector nodes.launch 2> >(grep -v TF_REPEATED_DATA buffer_core) \ No newline at end of file diff --git a/src/sim_pkg/launch/bash_scripts/rviz.sh b/src/sim_pkg/launch/bash_scripts/rviz.sh index e330796..5864807 100755 --- a/src/sim_pkg/launch/bash_scripts/rviz.sh +++ b/src/sim_pkg/launch/bash_scripts/rviz.sh @@ -1,10 +1,51 @@ #!/bin/bash -RVIZ_CONFIG=$(rospack find sim_pkg)/launch/bash_scripts/KOUMEKATRONOM.rviz +PKG_PATH=$(rospack find models_pkg) +if [ -z "$PKG_PATH" ]; then + echo "Error: models_pkg not found in ROS workspace." + exit 1 +fi -if [ ! -f "$RVIZ_CONFIG" ]; then - echo "RViz configuration file not found: $RVIZ_CONFIG" +TEXTURE_PATH="$PKG_PATH/track/materials/textures/new.png" + +if [ ! -f "$TEXTURE_PATH" ]; then + echo "Error: $TEXTURE_PATH does not exist." exit 1 fi -rviz -d "$RVIZ_CONFIG" 2> >(grep -v TF_REPEATED_DATA buffer_core) +echo "Found PNG file: $TEXTURE_PATH" + +# Dynamically generate a YAML file instead of using the creating one for just map +MAP_YAML_PATH="/tmp/map.yaml" +cat < "$MAP_YAML_PATH" +image: $TEXTURE_PATH +resolution: 0.002125 +origin: [0.0, 0.0, 0.0] +negate: 0 +occupied_thresh: 0.65 +free_thresh: 0.5 +EOF + +echo "Generated YAML file at: $MAP_YAML_PATH" +cat "$MAP_YAML_PATH" + +echo "Starting map_server with map: $MAP_YAML_PATH" +rosrun map_server map_server "$MAP_YAML_PATH" & + +sleep 2 +if ! rostopic list | grep -q "/map"; then + echo "Error: /map topic not found. Map server might have failed." + exit 1 +fi + +echo "/map topic successfully published." + +CONFIG_PATH="$(rospack find sim_pkg)/launch/bash_scripts/rviz_config.rviz" +if [ ! -f "$CONFIG_PATH" ]; then + echo "Error: RViz config file not found at $CONFIG_PATH." + exit 1 +fi + +echo "Resolved RViz config path: $CONFIG_PATH" + +rviz -d "$CONFIG_PATH" 2> >(grep -v TF_REPEATED_DATA buffer_core) diff --git a/src/sim_pkg/launch/bash_scripts/rviz_config.rviz b/src/sim_pkg/launch/bash_scripts/rviz_config.rviz new file mode 100644 index 0000000..e8516d3 --- /dev/null +++ b/src/sim_pkg/launch/bash_scripts/rviz_config.rviz @@ -0,0 +1,400 @@ +Panels: + - Class: rviz/Displays + Help Height: 78 + Name: Displays + Property Tree Widget: + Expanded: + - /Global Options1 + - /Status1 + - /TF1/Frames1 + - /PoseWithCovariance1/Covariance1 + - /PoseWithCovariance1/Covariance1/Orientation1 + - /Odometry1/Shape1 + - /Odometry1/Covariance1 + - /Odometry1/Covariance1/Position1 + Splitter Ratio: 0.570155918598175 + Tree Height: 1085 + - Class: rviz/Selection + Name: Selection + - Class: rviz/Tool Properties + Expanded: + - /2D Pose Estimate1 + - /2D Nav Goal1 + - /Publish Point1 + Name: Tool Properties + Splitter Ratio: 0.5886790156364441 + - Class: rviz/Views + Expanded: + - /Current View1 + Name: Views + Splitter Ratio: 0.5 + - Class: rviz/Time + Name: Time + SyncMode: 0 + SyncSource: "" +Preferences: + PromptSaveOnExit: true +Toolbars: + toolButtonStyle: 2 +Visualization Manager: + Class: "" + Displays: + - Alpha: 0.5 + Cell Size: 1 + Class: rviz/Grid + Color: 160; 160; 164 + Enabled: true + Line Style: + Line Width: 0.029999999329447746 + Value: Lines + Name: Grid + Normal Cell Count: 0 + Offset: + X: 0 + Y: 0 + Z: 0 + Plane: XY + Plane Cell Count: 10 + Reference Frame: + Value: true + - Alpha: 1 + Class: rviz/RobotModel + Collision Enabled: false + Enabled: true + Links: + All Links Enabled: true + Expand Joint Details: false + Expand Link Details: false + Expand Tree: false + Link Tree Style: Links in Alphabetic Order + automobile_camera_link_camera: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + automobile_car_body_link: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + automobile_chassis_link: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + automobile_lidar_lidar_link: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + automobile_steer_left_link_steer: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + automobile_steer_right_link_steer: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + automobile_wheel_front_left_link_rim: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + automobile_wheel_front_right_link_rim: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + automobile_wheel_rear_left_link_rim: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + automobile_wheel_rear_right_link_rim: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + Name: RobotModel + Robot Description: robot_description + TF Prefix: "" + Update Interval: 0 + Value: true + Visual Enabled: true + - Class: rviz/TF + Enabled: true + Filter (blacklist): "" + Filter (whitelist): "" + Frame Timeout: 15 + Frames: + All Enabled: false + automobile_camera_link_camera: + Value: true + automobile_car_body_link: + Value: true + automobile_chassis_link: + Value: true + automobile_lidar_lidar_link: + Value: true + automobile_steer_left_link_steer: + Value: true + automobile_steer_right_link_steer: + Value: true + automobile_wheel_front_left_link_rim: + Value: true + automobile_wheel_front_right_link_rim: + Value: true + automobile_wheel_rear_left_link_rim: + Value: true + automobile_wheel_rear_right_link_rim: + Value: true + map: + Value: true + odom: + Value: true + Marker Alpha: 1 + Marker Scale: 1 + Name: TF + Show Arrows: true + Show Axes: true + Show Names: false + Tree: + map: + odom: + automobile_chassis_link: + automobile_camera_link_camera: + {} + automobile_car_body_link: + {} + automobile_lidar_lidar_link: + {} + automobile_steer_left_link_steer: + automobile_wheel_front_left_link_rim: + {} + automobile_steer_right_link_steer: + automobile_wheel_front_right_link_rim: + {} + automobile_wheel_rear_left_link_rim: + {} + automobile_wheel_rear_right_link_rim: + {} + Update Interval: 0 + Value: true + - Circles color: 170; 0; 0 + Class: Obstacles + Enabled: true + Margin color: 0; 170; 0 + Name: Obstacles + Opacity: 0.75 + Queue Size: 10 + Segments color: 170; 170; 0 + Segments thickness: 0.029999999329447746 + Topic: /obstacles + Unreliable: false + Value: true + - Alpha: 1 + Autocompute Intensity Bounds: true + Autocompute Value Bounds: + Max Value: 10 + Min Value: -10 + Value: true + Axis: Z + Channel Name: intensity + Class: rviz/LaserScan + Color: 255; 255; 255 + Color Transformer: Intensity + Decay Time: 0 + Enabled: true + Invert Rainbow: false + Max Color: 255; 255; 255 + Min Color: 0; 0; 0 + Name: LaserScan + Position Transformer: XYZ + Queue Size: 10 + Selectable: true + Size (Pixels): 3 + Size (m): 0.009999999776482582 + Style: Flat Squares + Topic: /zed2i/depth/scan + Unreliable: false + Use Fixed Frame: true + Use rainbow: true + Value: true + - Alpha: 1 + Class: rviz_plugin_tutorials/Imu + Color: 115; 210; 22 + Enabled: true + History Length: 1 + Name: Imu + Queue Size: 10 + Topic: /automobile/IMU + Unreliable: false + Value: true + - Alpha: 1 + Axes Length: 1 + Axes Radius: 0.10000000149011612 + Class: rviz/PoseWithCovariance + Color: 255; 25; 0 + Covariance: + Orientation: + Alpha: 0.5 + Color: 255; 255; 127 + Color Style: Unique + Frame: Local + Offset: 1 + Scale: 1 + Value: false + Position: + Alpha: 0.30000001192092896 + Color: 204; 51; 204 + Scale: 1 + Value: true + Value: true + Enabled: true + Head Length: 0.30000001192092896 + Head Radius: 0.10000000149011612 + Name: PoseWithCovariance + Queue Size: 10 + Shaft Length: 1 + Shaft Radius: 0.05000000074505806 + Shape: Arrow + Topic: /automobile/localisation + Unreliable: false + Value: true + - Angle Tolerance: 0.10000000149011612 + Class: rviz/Odometry + Covariance: + Orientation: + Alpha: 0.5 + Color: 255; 255; 127 + Color Style: Unique + Frame: Local + Offset: 1 + Scale: 1 + Value: false + Position: + Alpha: 0.20000000298023224 + Color: 204; 51; 204 + Scale: 0.10000000149011612 + Value: true + Value: true + Enabled: true + Keep: 100 + Name: Odometry + Position Tolerance: 0.10000000149011612 + Queue Size: 10 + Shape: + Alpha: 0.20000000298023224 + Axes Length: 1 + Axes Radius: 0.10000000149011612 + Color: 255; 25; 0 + Head Length: 0.30000001192092896 + Head Radius: 0.10000000149011612 + Shaft Length: 1 + Shaft Radius: 0.05000000074505806 + Value: Arrow + Topic: /odometry/filtered + Unreliable: false + Value: true + - Acceleration properties: + Acc. vector alpha: 1 + Acc. vector color: 255; 0; 0 + Acc. vector scale: 1 + Derotate acceleration: true + Enable acceleration: false + Axes properties: + Axes scale: 1 + Enable axes: true + Box properties: + Box alpha: 1 + Box color: 255; 0; 0 + Enable box: true + x_scale: 0.30000001192092896 + y_scale: 0.10000000149011612 + z_scale: 0.10000000149011612 + Class: rviz_imu_plugin/Imu + Enabled: true + Name: Imu + Queue Size: 10 + Topic: /automobile/IMU + Unreliable: false + Value: true + fixed_frame_orientation: true + - Alpha: 0.699999988079071 + Class: rviz/Map + Color Scheme: map + Draw Behind: false + Enabled: true + Name: Map + Topic: /map + Unreliable: false + Use Timestamp: false + Value: true + Enabled: true + Global Options: + Background Color: 48; 48; 48 + Default Light: true + Fixed Frame: map + Frame Rate: 30 + Name: root + Tools: + - Class: rviz/Interact + Hide Inactive Objects: true + - Class: rviz/MoveCamera + - Class: rviz/Select + - Class: rviz/FocusCamera + - Class: rviz/Measure + - Class: rviz/SetInitialPose + Theta std deviation: 0.2617993950843811 + Topic: /initialpose + X std deviation: 0.5 + Y std deviation: 0.5 + - Class: rviz/SetGoal + Topic: /move_base_simple/goal + - Class: rviz/PublishPoint + Single click: true + Topic: /clicked_point + Value: true + Views: + Current: + Class: rviz/Orbit + Distance: 6.44405460357666 + Enable Stereo Rendering: + Stereo Eye Separation: 0.05999999865889549 + Stereo Focal Distance: 1 + Swap Stereo Eyes: false + Value: false + Field of View: 0.7853981852531433 + Focal Point: + X: 0.8365225791931152 + Y: -0.18211668729782104 + Z: 0.07686444371938705 + Focal Shape Fixed Size: true + Focal Shape Size: 0.05000000074505806 + Invert Z Axis: false + Name: Current View + Near Clip Distance: 0.009999999776482582 + Pitch: 1.5697963237762451 + Target Frame: automobile_chassis_link + Yaw: 4.708574295043945 + Saved: ~ +Window Geometry: + Displays: + collapsed: false + Height: 1376 + Hide Left Dock: false + Hide Right Dock: true + QMainWindow State: 000000ff00000000fd0000000400000000000001c3000004c6fc0200000008fb0000001200530065006c0065006300740069006f006e00000001e10000009b0000005c00fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000001ed000001df00000185000000a3fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afb000000100044006900730070006c006100790073010000003b000004c6000000c700fffffffb0000002000730065006c0065006300740069006f006e00200062007500660066006500720200000138000000aa0000023a00000294fb00000014005700690064006500530074006500720065006f02000000e6000000d2000003ee0000030bfb0000000c004b0069006e0065006300740200000186000001060000030c00000261000000010000010f000004c2fc0200000003fb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fb0000000a00560069006500770073000000003d000004c2000000a000fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e10000019700000003000009ba0000003efc0100000002fb0000000800540069006d00650100000000000009ba0000030700fffffffb0000000800540069006d00650100000000000004500000000000000000000007f1000004c600000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000 + Selection: + collapsed: false + Time: + collapsed: false + Tool Properties: + collapsed: false + Views: + collapsed: true + Width: 2490 + X: 2630 + Y: 27 diff --git a/src/sim_pkg/launch/map_with_all_objects.launch b/src/sim_pkg/launch/map_with_all_objects.launch index 16ac6a0..fb96e66 100644 --- a/src/sim_pkg/launch/map_with_all_objects.launch +++ b/src/sim_pkg/launch/map_with_all_objects.launch @@ -2,7 +2,7 @@ - + @@ -15,13 +15,13 @@ - + - + diff --git a/src/sim_pkg/launch/map_with_all_objects_REC.launch b/src/sim_pkg/launch/map_with_all_objects_REC.launch index 04df511..225a0c2 100644 --- a/src/sim_pkg/launch/map_with_all_objects_REC.launch +++ b/src/sim_pkg/launch/map_with_all_objects_REC.launch @@ -21,7 +21,7 @@ - + diff --git a/src/sim_pkg/launch/map_with_car.launch b/src/sim_pkg/launch/map_with_car.launch index 153d354..692bca1 100644 --- a/src/sim_pkg/launch/map_with_car.launch +++ b/src/sim_pkg/launch/map_with_car.launch @@ -1,19 +1,20 @@ - - + + - - + + + - - + + - + + args="0 0 0 0 0 0 map odom 100" /> + command="$(find xacro)/xacro '$(find models_pkg)/rcCar_assembly/model.urdf'"/> @@ -22,13 +23,18 @@ - - - - - + - + - + + + + + + + + + + diff --git a/src/sim_pkg/launch/map_with_car_REC.launch b/src/sim_pkg/launch/map_with_car_REC.launch index af8869e..019df48 100644 --- a/src/sim_pkg/launch/map_with_car_REC.launch +++ b/src/sim_pkg/launch/map_with_car_REC.launch @@ -2,11 +2,11 @@ - + - + diff --git a/src/sim_pkg/launch/sublaunchers/pedestrian_objects.launch b/src/sim_pkg/launch/sublaunchers/pedestrian_objects.launch index 2cf4fb9..cf49ec5 100644 --- a/src/sim_pkg/launch/sublaunchers/pedestrian_objects.launch +++ b/src/sim_pkg/launch/sublaunchers/pedestrian_objects.launch @@ -7,19 +7,19 @@ name = "PED_OBJ_KL" pkg = "gazebo_ros" type = "spawn_model" - args = "-file $(find models_pkg)/pedestrian_object/model.sdf -sdf -model PED_OBJ_KL -x 6.427 -y -1.422 -z 0 -R 0 -P 0 -Y 3.141591"/> + args = "-file $(find models_pkg)/pedestrian_object/model.sdf -sdf -model PED_OBJ_KL -x 8.427 -y 1.001 -z 0 -R 0 -P 0 -Y 0"/> + args = "-file $(find models_pkg)/pedestrian_object/model.sdf -sdf -model PED_OBJ_MN -x 9.25 -y 3.295 -z 0 -R 0 -P 0 -Y 3.13491"/> + args = "-file $(find models_pkg)/pedestrian_object/model.sdf -sdf -model PED_OBJ_AD_H -x 16.74 -y 2.55 -z 0 -R 0 -P 0 -Y 1.56"/> diff --git a/src/sim_pkg/launch/sublaunchers/traffic_lights.launch b/src/sim_pkg/launch/sublaunchers/traffic_lights.launch index 7aca684..78d0c65 100644 --- a/src/sim_pkg/launch/sublaunchers/traffic_lights.launch +++ b/src/sim_pkg/launch/sublaunchers/traffic_lights.launch @@ -12,24 +12,24 @@ name = "start" pkg = "gazebo_ros" type = "spawn_model" - args = "-file $(find models_pkg)/traffic_light/model.sdf -sdf -model start -x 1.132 -y -14.403 -z 0 -R 0 -P 0 -Y 4.712388"/> + args = "-file $(find models_pkg)/traffic_light/model.sdf -sdf -model start -x 4.21 -y 3.303 -z 0 -R 0 -P 0 -Y -3.13"/> + args = "-file $(find models_pkg)/traffic_light/model.sdf -sdf -model master -x 5.630 -y 4.292 -z 0 -R 0 -P 0 -Y 0"/> + args = "-file $(find models_pkg)/traffic_light/model.sdf -sdf -model slave -x 2.23 -y 4.54 -z 0 -R 0 -P 0 -Y 1.55"/> - + args = "-file $(find models_pkg)/traffic_light/model.sdf -sdf -model antimaster -x -0.05 -y -11.483 -z 0 -R 0 -P 0 -Y 4.712388"/> --> diff --git a/src/sim_pkg/worlds/world_with_baha.world b/src/sim_pkg/worlds/world_with_baha.world new file mode 100644 index 0000000..f2fcc1b --- /dev/null +++ b/src/sim_pkg/worlds/world_with_baha.world @@ -0,0 +1,10936 @@ + + + 0 0 -9.8 + + 0 0 2 0 -0 0 + 125 125 125 125 + 125 125 125 125 + + 20 + 0.3 + 0 + 0 + + 0 + 0 0 -1 + + 0 + 0 + 0 + + + + 15 0 2 0 -0 0 + 125 125 125 125 + 125 125 125 125 + + 20 + 0.3 + 0 + 0 + + 0 + 0 0 -1 + + 0 + 0 + 0 + + + + 0 -15 2 0 -0 0 + 125 125 125 125 + 125 125 125 125 + + 20 + 0.3 + 0 + 0 + + 0 + 0 0 -1 + + 0 + 0 + 0 + + + + 15 -15 2 0 -0 0 + 125 125 125 125 + 125 125 125 125 + + 50 + 0.3 + 0 + 0 + + 0 + 0 0 -1 + + 0 + 0 + 0 + + + + 1 + + + + + 0 0 1 + 20.48 13.62 + + + + + 65535 + + + + + 1 + 1 + + + + + + + + 10 + + + 1 + 0 + + + 0 0 1 + 20.48 13.62 + + + + 1 1 1 1 + 1 1 1 1 + 0 0 0 0 + 1 1 1 1 + + + + 0 + 0 + 0 + + 10.24 6.81 0 0 -0 0 + + 6e-06 2.3e-05 -4.2e-05 + + + 0.001 + 1 + 1000 + + + 0.4 0.4 0.4 1 + 0.7 0.7 0.7 1 + 1 + + + + EARTH_WGS84 + 0 + 0 + 0 + 0 + + + 1 + + 0 0 0 0 -0 0 + + + + 0.26244 + + 0.00283444 + 0 + 0 + 0.00039867 + 0 + 0.00323293 + + 0 0 0 0 -0 0 + + 0 0 0 0 -0 0 + 1 + 0 + 0 + + 0 0 -0.0065 0 -0 0 + + + model://chassis/meshes/chassisV5.dae + 1 1 1 + + + + 1 + + __default__ + + 0.6 0.6 0.6 1 + 0.85 0.85 0.85 1 + 0.6 0.6 0.6 1 + 0 0 0 1 + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + 0.135 0.36 0.002 + + + + + + 1 + 1 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+13 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+13 + 1 + + + + + 0 + + + -0.152 0.081 0 0 -0 0 + + + + 0.038 + + 1.283e-05 + 0 + 0 + 1.283e-05 + 0 + 2.069e-05 + + 0 0 0 0 -0 0 + + 0 0 0 1.5708 -0 0 + 1 + 0 + 0 + + 0 0 0 -1.5708 0 0 + + + model://wheel_left/meshes/rim.dae + 1 1 1 + + + + 1 + + __default__ + + 0.2 0.2 0.2 1 + 0.3 0.3 0.3 1 + 0.5 0.5 0.5 1 + 0 0 0 0 + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + 0.033 + 0.028 + + + + + + 0.7 + 0.7 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+07 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+07 + 1 + + + + + 0 + + + -0.152 -0.081 0 0 -0 0 + + + + 0.038 + + 1.283e-05 + 0 + 0 + 1.283e-05 + 0 + 2.069e-05 + + 0 0 0 0 -0 0 + + 0 0 0 1.5708 -0 0 + 1 + 0 + 0 + + 0 0 0 -1.5708 0 0 + + + model://wheel_right/meshes/rim.dae + 1 1 1 + + + + 1 + + __default__ + + 0.2 0.2 0.2 1 + 0.3 0.3 0.3 1 + 0.5 0.5 0.5 1 + 0 0 0 0 + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + 0.033 + 0.028 + + + + + + 0.7 + 0.7 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+07 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+07 + 1 + + + + + 0 + + + 0.112 0.0725 0 0 -0 0 + + + + 0.001 + + 0.00014583 + 0 + 0 + 0.00014583 + 0 + 0.000125 + + 0 0 0 0 -0 0 + + 0 0 0 1.5708 -0 0 + 1 + 0 + 0 + 0 + + 0 0 0 0 -0 0 + + + 0.001 + 0.01 + + + + 1 + + + __default__ + + 0.3 0.3 0.3 1 + 0.7 0.7 0.7 1 + 0.01 0.01 0.01 1 + 0 0 0 1 + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + 0.001 + 0.01 + + + + + + 1 + 1 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+13 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+13 + 1 + + + + + + + 0.112 -0.0725 0 0 -0 0 + + + + 0.001 + + 0.00014583 + 0 + 0 + 0.00014583 + 0 + 0.000125 + + 0 0 0 0 -0 0 + + 0 0 0 1.5708 -0 0 + 1 + 0 + 0 + 0 + + 0 0 0 0 -0 0 + + + 0.001 + 0.01 + + + + 1 + + + __default__ + + 0.3 0.3 0.3 1 + 0.7 0.7 0.7 1 + 0.01 0.01 0.01 1 + 0 0 0 1 + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + 0.001 + 0.01 + + + + + + 1 + 1 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+13 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+13 + 1 + + + + + + + 0.118 0.081 0 0 -0 0 + + + + 0.038 + + 1.283e-05 + 0 + 0 + 1.283e-05 + 0 + 2.069e-05 + + 0 0 0 0 -0 0 + + 0 0 0 1.5708 -0 0 + 1 + 0 + 0 + + 0 0 0 -1.5708 0 0 + + + model://wheel_left/meshes/rim.dae + 1 1 1 + + + + 1 + + __default__ + + 0.2 0.2 0.2 1 + 0.3 0.3 0.3 1 + 0.5 0.5 0.5 1 + 0 0 0 0 + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + 0.033 + 0.028 + + + + + + 0.7 + 0.7 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+07 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+07 + 1 + + + + + 0 + + + 0.118 -0.081 0 0 -0 0 + + + + 0.038 + + 1.283e-05 + 0 + 0 + 1.283e-05 + 0 + 2.069e-05 + + 0 0 0 0 -0 0 + + 0 0 0 1.5708 -0 0 + 1 + 0 + 0 + + 0 0 0 -1.5708 0 0 + + + model://wheel_right/meshes/rim.dae + 1 1 1 + + + + 1 + + __default__ + + 0.2 0.2 0.2 1 + 0.3 0.3 0.3 1 + 0.5 0.5 0.5 1 + 0 0 0 0 + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + 0.033 + 0.028 + + + + + + 0.7 + 0.7 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+07 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+07 + 1 + + + + + 0 + + + -0.024 0 -0.028 0 -0 0 + + + + 1 + + 0.166667 + 0 + 0 + 0.166667 + 0 + 0.166667 + + + 0 -0 0 0 -0 0 + + 0 0 0 0 -0 0 + + + model://car_body/meshes/CarBody.dae + 1 1 1 + + + + + + 0 + 1 + + 0 + 0 + 0 + + 0 + 1 + + chassis::link + wheel_rear_left::link_rim + 0 0 0 0 -0 0 + + + chassis::link + wheel_rear_right::link_rim + 0 0 0 0 -0 0 + + + chassis::link + wheel_front_right::link_rim + 0 0 0 0 -0 0 + + + chassis::link + wheel_front_left::link_rim + 0 0 0 0 -0 0 + + + chassis::link + car_body::link + 0 0 0 0 -0 0 + + 9.42884 1.04599 0 0 -0 0 + + + 1 + + 0 0 0 0 -0 0 + + + + 0.26244 + + 0.00283444 + 0 + 0 + 0.00039867 + 0 + 0.00323293 + + 0 0 0 0 -0 0 + + 0 0 0 0 -0 0 + 1 + 0 + 0 + + 0 0 -0.0065 0 -0 0 + + + model://chassis/meshes/chassisV5.dae + 1 1 1 + + + + 1 + + __default__ + + 0.6 0.6 0.6 1 + 0.85 0.85 0.85 1 + 0.6 0.6 0.6 1 + 0 0 0 1 + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + 0.135 0.36 0.002 + + + + + + 1 + 1 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+13 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+13 + 1 + + + + + 0 + + + -0.152 0.081 0 0 -0 0 + + + + 0.038 + + 1.283e-05 + 0 + 0 + 1.283e-05 + 0 + 2.069e-05 + + 0 0 0 0 -0 0 + + 0 0 0 1.5708 -0 0 + 1 + 0 + 0 + + 0 0 0 -1.5708 0 0 + + + model://wheel_left/meshes/rim.dae + 1 1 1 + + + + 1 + + __default__ + + 0.2 0.2 0.2 1 + 0.3 0.3 0.3 1 + 0.5 0.5 0.5 1 + 0 0 0 0 + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + 0.033 + 0.028 + + + + + + 0.7 + 0.7 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+07 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+07 + 1 + + + + + 0 + + + -0.152 -0.081 0 0 -0 0 + + + + 0.038 + + 1.283e-05 + 0 + 0 + 1.283e-05 + 0 + 2.069e-05 + + 0 0 0 0 -0 0 + + 0 0 0 1.5708 -0 0 + 1 + 0 + 0 + + 0 0 0 -1.5708 0 0 + + + model://wheel_right/meshes/rim.dae + 1 1 1 + + + + 1 + + __default__ + + 0.2 0.2 0.2 1 + 0.3 0.3 0.3 1 + 0.5 0.5 0.5 1 + 0 0 0 0 + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + 0.033 + 0.028 + + + + + + 0.7 + 0.7 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+07 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+07 + 1 + + + + + 0 + + + 0.112 0.0725 0 0 -0 0 + + + + 0.001 + + 0.00014583 + 0 + 0 + 0.00014583 + 0 + 0.000125 + + 0 0 0 0 -0 0 + + 0 0 0 1.5708 -0 0 + 1 + 0 + 0 + 0 + + 0 0 0 0 -0 0 + + + 0.001 + 0.01 + + + + 1 + + + __default__ + + 0.3 0.3 0.3 1 + 0.7 0.7 0.7 1 + 0.01 0.01 0.01 1 + 0 0 0 1 + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + 0.001 + 0.01 + + + + + + 1 + 1 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+13 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+13 + 1 + + + + + + + 0.112 -0.0725 0 0 -0 0 + + + + 0.001 + + 0.00014583 + 0 + 0 + 0.00014583 + 0 + 0.000125 + + 0 0 0 0 -0 0 + + 0 0 0 1.5708 -0 0 + 1 + 0 + 0 + 0 + + 0 0 0 0 -0 0 + + + 0.001 + 0.01 + + + + 1 + + + __default__ + + 0.3 0.3 0.3 1 + 0.7 0.7 0.7 1 + 0.01 0.01 0.01 1 + 0 0 0 1 + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + 0.001 + 0.01 + + + + + + 1 + 1 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+13 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+13 + 1 + + + + + + + 0.118 0.081 0 0 -0 0 + + + + 0.038 + + 1.283e-05 + 0 + 0 + 1.283e-05 + 0 + 2.069e-05 + + 0 0 0 0 -0 0 + + 0 0 0 1.5708 -0 0 + 1 + 0 + 0 + + 0 0 0 -1.5708 0 0 + + + model://wheel_left/meshes/rim.dae + 1 1 1 + + + + 1 + + __default__ + + 0.2 0.2 0.2 1 + 0.3 0.3 0.3 1 + 0.5 0.5 0.5 1 + 0 0 0 0 + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + 0.033 + 0.028 + + + + + + 0.7 + 0.7 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+07 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+07 + 1 + + + + + 0 + + + 0.118 -0.081 0 0 -0 0 + + + + 0.038 + + 1.283e-05 + 0 + 0 + 1.283e-05 + 0 + 2.069e-05 + + 0 0 0 0 -0 0 + + 0 0 0 1.5708 -0 0 + 1 + 0 + 0 + + 0 0 0 -1.5708 0 0 + + + model://wheel_right/meshes/rim.dae + 1 1 1 + + + + 1 + + __default__ + + 0.2 0.2 0.2 1 + 0.3 0.3 0.3 1 + 0.5 0.5 0.5 1 + 0 0 0 0 + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + 0.033 + 0.028 + + + + + + 0.7 + 0.7 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+07 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+07 + 1 + + + + + 0 + + + -0.024 0 -0.028 0 -0 0 + + + + 1 + + 0.166667 + 0 + 0 + 0.166667 + 0 + 0.166667 + + + 0 -0 0 0 -0 0 + + 0 0 0 0 -0 0 + + + model://car_body/meshes/CarBody.dae + 1 1 1 + + + + + + 0 + 1 + + 0 + 0 + 0 + + 0 + 1 + + chassis::link + wheel_rear_left::link_rim + 0 0 0 0 -0 0 + + + chassis::link + wheel_rear_right::link_rim + 0 0 0 0 -0 0 + + + chassis::link + wheel_front_right::link_rim + 0 0 0 0 -0 0 + + + chassis::link + wheel_front_left::link_rim + 0 0 0 0 -0 0 + + + chassis::link + car_body::link + 0 0 0 0 -0 0 + + 9.40936 0.360421 0 0 -0 0 + + + 1 + + 0 0 0 0 -0 0 + + + + 0.26244 + + 0.00283444 + 0 + 0 + 0.00039867 + 0 + 0.00323293 + + 0 0 0 0 -0 0 + + 0 0 0 0 -0 0 + 1 + 0 + 0 + + 0 0 -0.0065 0 -0 0 + + + model://chassis/meshes/chassisV5.dae + 1 1 1 + + + + 1 + + __default__ + + 0.6 0.6 0.6 1 + 0.85 0.85 0.85 1 + 0.6 0.6 0.6 1 + 0 0 0 1 + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + 0.135 0.36 0.002 + + + + + + 1 + 1 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+13 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+13 + 1 + + + + + 0 + + + -0.152 0.081 0 0 -0 0 + + + + 0.038 + + 1.283e-05 + 0 + 0 + 1.283e-05 + 0 + 2.069e-05 + + 0 0 0 0 -0 0 + + 0 0 0 1.5708 -0 0 + 1 + 0 + 0 + + 0 0 0 -1.5708 0 0 + + + model://wheel_left/meshes/rim.dae + 1 1 1 + + + + 1 + + __default__ + + 0.2 0.2 0.2 1 + 0.3 0.3 0.3 1 + 0.5 0.5 0.5 1 + 0 0 0 0 + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + 0.033 + 0.028 + + + + + + 0.7 + 0.7 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+07 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+07 + 1 + + + + + 0 + + + -0.152 -0.081 0 0 -0 0 + + + + 0.038 + + 1.283e-05 + 0 + 0 + 1.283e-05 + 0 + 2.069e-05 + + 0 0 0 0 -0 0 + + 0 0 0 1.5708 -0 0 + 1 + 0 + 0 + + 0 0 0 -1.5708 0 0 + + + model://wheel_right/meshes/rim.dae + 1 1 1 + + + + 1 + + __default__ + + 0.2 0.2 0.2 1 + 0.3 0.3 0.3 1 + 0.5 0.5 0.5 1 + 0 0 0 0 + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + 0.033 + 0.028 + + + + + + 0.7 + 0.7 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+07 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+07 + 1 + + + + + 0 + + + 0.112 0.0725 0 0 -0 0 + + + + 0.001 + + 0.00014583 + 0 + 0 + 0.00014583 + 0 + 0.000125 + + 0 0 0 0 -0 0 + + 0 0 0 1.5708 -0 0 + 1 + 0 + 0 + 0 + + 0 0 0 0 -0 0 + + + 0.001 + 0.01 + + + + 1 + + + __default__ + + 0.3 0.3 0.3 1 + 0.7 0.7 0.7 1 + 0.01 0.01 0.01 1 + 0 0 0 1 + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + 0.001 + 0.01 + + + + + + 1 + 1 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+13 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+13 + 1 + + + + + + + 0.112 -0.0725 0 0 -0 0 + + + + 0.001 + + 0.00014583 + 0 + 0 + 0.00014583 + 0 + 0.000125 + + 0 0 0 0 -0 0 + + 0 0 0 1.5708 -0 0 + 1 + 0 + 0 + 0 + + 0 0 0 0 -0 0 + + + 0.001 + 0.01 + + + + 1 + + + __default__ + + 0.3 0.3 0.3 1 + 0.7 0.7 0.7 1 + 0.01 0.01 0.01 1 + 0 0 0 1 + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + 0.001 + 0.01 + + + + + + 1 + 1 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+13 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+13 + 1 + + + + + + + 0.118 0.081 0 0 -0 0 + + + + 0.038 + + 1.283e-05 + 0 + 0 + 1.283e-05 + 0 + 2.069e-05 + + 0 0 0 0 -0 0 + + 0 0 0 1.5708 -0 0 + 1 + 0 + 0 + + 0 0 0 -1.5708 0 0 + + + model://wheel_left/meshes/rim.dae + 1 1 1 + + + + 1 + + __default__ + + 0.2 0.2 0.2 1 + 0.3 0.3 0.3 1 + 0.5 0.5 0.5 1 + 0 0 0 0 + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + 0.033 + 0.028 + + + + + + 0.7 + 0.7 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+07 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+07 + 1 + + + + + 0 + + + 0.118 -0.081 0 0 -0 0 + + + + 0.038 + + 1.283e-05 + 0 + 0 + 1.283e-05 + 0 + 2.069e-05 + + 0 0 0 0 -0 0 + + 0 0 0 1.5708 -0 0 + 1 + 0 + 0 + + 0 0 0 -1.5708 0 0 + + + model://wheel_right/meshes/rim.dae + 1 1 1 + + + + 1 + + __default__ + + 0.2 0.2 0.2 1 + 0.3 0.3 0.3 1 + 0.5 0.5 0.5 1 + 0 0 0 0 + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + 0.033 + 0.028 + + + + + + 0.7 + 0.7 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+07 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+07 + 1 + + + + + 0 + + + -0.024 0 -0.028 0 -0 0 + + + + 1 + + 0.166667 + 0 + 0 + 0.166667 + 0 + 0.166667 + + + 0 -0 0 0 -0 0 + + 0 0 0 0 -0 0 + + + model://car_body/meshes/CarBody.dae + 1 1 1 + + + + + + 0 + 1 + + 0 + 0 + 0 + + 0 + 1 + + chassis::link + wheel_rear_left::link_rim + 0 0 0 0 -0 0 + + + chassis::link + wheel_rear_right::link_rim + 0 0 0 0 -0 0 + + + chassis::link + wheel_front_right::link_rim + 0 0 0 0 -0 0 + + + chassis::link + wheel_front_left::link_rim + 0 0 0 0 -0 0 + + + chassis::link + car_body::link + 0 0 0 0 -0 0 + + 10.2341 0.321199 0 0 -0 0 + + + 1 + + 0 0 0 0 -0 0 + + + + 0.26244 + + 0.00283444 + 0 + 0 + 0.00039867 + 0 + 0.00323293 + + 0 0 0 0 -0 0 + + 0 0 0 0 -0 0 + 1 + 0 + 0 + + 0 0 -0.0065 0 -0 0 + + + model://chassis/meshes/chassisV5.dae + 1 1 1 + + + + 1 + + __default__ + + 0.6 0.6 0.6 1 + 0.85 0.85 0.85 1 + 0.6 0.6 0.6 1 + 0 0 0 1 + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + 0.135 0.36 0.002 + + + + + + 1 + 1 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+13 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+13 + 1 + + + + + 0 + + + -0.152 0.081 0 0 -0 0 + + + + 0.038 + + 1.283e-05 + 0 + 0 + 1.283e-05 + 0 + 2.069e-05 + + 0 0 0 0 -0 0 + + 0 0 0 1.5708 -0 0 + 1 + 0 + 0 + + 0 0 0 -1.5708 0 0 + + + model://wheel_left/meshes/rim.dae + 1 1 1 + + + + 1 + + __default__ + + 0.2 0.2 0.2 1 + 0.3 0.3 0.3 1 + 0.5 0.5 0.5 1 + 0 0 0 0 + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + 0.033 + 0.028 + + + + + + 0.7 + 0.7 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+07 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+07 + 1 + + + + + 0 + + + -0.152 -0.081 0 0 -0 0 + + + + 0.038 + + 1.283e-05 + 0 + 0 + 1.283e-05 + 0 + 2.069e-05 + + 0 0 0 0 -0 0 + + 0 0 0 1.5708 -0 0 + 1 + 0 + 0 + + 0 0 0 -1.5708 0 0 + + + model://wheel_right/meshes/rim.dae + 1 1 1 + + + + 1 + + __default__ + + 0.2 0.2 0.2 1 + 0.3 0.3 0.3 1 + 0.5 0.5 0.5 1 + 0 0 0 0 + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + 0.033 + 0.028 + + + + + + 0.7 + 0.7 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+07 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+07 + 1 + + + + + 0 + + + 0.112 0.0725 0 0 -0 0 + + + + 0.001 + + 0.00014583 + 0 + 0 + 0.00014583 + 0 + 0.000125 + + 0 0 0 0 -0 0 + + 0 0 0 1.5708 -0 0 + 1 + 0 + 0 + 0 + + 0 0 0 0 -0 0 + + + 0.001 + 0.01 + + + + 1 + + + __default__ + + 0.3 0.3 0.3 1 + 0.7 0.7 0.7 1 + 0.01 0.01 0.01 1 + 0 0 0 1 + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + 0.001 + 0.01 + + + + + + 1 + 1 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+13 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+13 + 1 + + + + + + + 0.112 -0.0725 0 0 -0 0 + + + + 0.001 + + 0.00014583 + 0 + 0 + 0.00014583 + 0 + 0.000125 + + 0 0 0 0 -0 0 + + 0 0 0 1.5708 -0 0 + 1 + 0 + 0 + 0 + + 0 0 0 0 -0 0 + + + 0.001 + 0.01 + + + + 1 + + + __default__ + + 0.3 0.3 0.3 1 + 0.7 0.7 0.7 1 + 0.01 0.01 0.01 1 + 0 0 0 1 + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + 0.001 + 0.01 + + + + + + 1 + 1 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+13 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+13 + 1 + + + + + + + 0.118 0.081 0 0 -0 0 + + + + 0.038 + + 1.283e-05 + 0 + 0 + 1.283e-05 + 0 + 2.069e-05 + + 0 0 0 0 -0 0 + + 0 0 0 1.5708 -0 0 + 1 + 0 + 0 + + 0 0 0 -1.5708 0 0 + + + model://wheel_left/meshes/rim.dae + 1 1 1 + + + + 1 + + __default__ + + 0.2 0.2 0.2 1 + 0.3 0.3 0.3 1 + 0.5 0.5 0.5 1 + 0 0 0 0 + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + 0.033 + 0.028 + + + + + + 0.7 + 0.7 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+07 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+07 + 1 + + + + + 0 + + + 0.118 -0.081 0 0 -0 0 + + + + 0.038 + + 1.283e-05 + 0 + 0 + 1.283e-05 + 0 + 2.069e-05 + + 0 0 0 0 -0 0 + + 0 0 0 1.5708 -0 0 + 1 + 0 + 0 + + 0 0 0 -1.5708 0 0 + + + model://wheel_right/meshes/rim.dae + 1 1 1 + + + + 1 + + __default__ + + 0.2 0.2 0.2 1 + 0.3 0.3 0.3 1 + 0.5 0.5 0.5 1 + 0 0 0 0 + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + 0.033 + 0.028 + + + + + + 0.7 + 0.7 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+07 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+07 + 1 + + + + + 0 + + + -0.024 0 -0.028 0 -0 0 + + + + 1 + + 0.166667 + 0 + 0 + 0.166667 + 0 + 0.166667 + + + 0 -0 0 0 -0 0 + + 0 0 0 0 -0 0 + + + model://car_body/meshes/CarBody.dae + 1 1 1 + + + + + + 0 + 1 + + 0 + 0 + 0 + + 0 + 1 + + chassis::link + wheel_rear_left::link_rim + 0 0 0 0 -0 0 + + + chassis::link + wheel_rear_right::link_rim + 0 0 0 0 -0 0 + + + chassis::link + wheel_front_right::link_rim + 0 0 0 0 -0 0 + + + chassis::link + wheel_front_left::link_rim + 0 0 0 0 -0 0 + + + chassis::link + car_body::link + 0 0 0 0 -0 0 + + 10.94 1.03535 0 0 -0 0 + + + 5451 101000000 + 41 977322941 + 1725387174 296537213 + 40995 + + 9.42884 1.28014 0.021752 0 -0 0 + 1 1 1 + + 9.40484 1.28014 -0.006248 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 9.42884 1.28014 0.021752 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 9.54084 1.35264 0.021752 1.5708 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 9.54084 1.20764 0.021752 1.5708 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 9.54684 1.36114 0.021752 1.5708 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 9.54684 1.19914 0.021752 1.5708 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 9.27684 1.36114 0.021752 1.5708 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 9.27684 1.19914 0.021752 1.5708 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + + 9.40789 0.558898 0.035446 0 -0 0 + 1 1 1 + + 9.3839 0.558898 0.007446 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 9.40789 0.558898 0.035446 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 9.51989 0.631398 0.035446 1.5708 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 9.51989 0.486398 0.035446 1.5708 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 9.5259 0.639898 0.035446 1.5708 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 9.5259 0.477898 0.035446 1.5708 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 9.2559 0.639898 0.035446 1.5708 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 9.2559 0.477898 0.035446 1.5708 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + + 10.2341 0.558076 0.023619 0 -0 0 + 1 1 1 + + 10.2101 0.558076 -0.004381 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 10.2341 0.558076 0.023619 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 10.3461 0.630576 0.023619 1.5708 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 10.3461 0.485576 0.023619 1.5708 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 10.3521 0.639076 0.023619 1.5708 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 10.3521 0.477076 0.023619 1.5708 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 10.0821 0.639076 0.023619 1.5708 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 10.0821 0.477076 0.023619 1.5708 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + + 10.9954 1.28412 0.016283 0 -0 0 + 1 1 1 + + 10.9714 1.28412 -0.011717 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 10.9954 1.28412 0.016283 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 11.1074 1.35662 0.016283 1.5708 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 11.1074 1.21163 0.016283 1.5708 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 11.1134 1.36512 0.016283 1.5708 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 11.1134 1.20313 0.016283 1.5708 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 10.8434 1.36512 0.016283 1.5708 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 10.8434 1.20313 0.016283 1.5708 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + + 10.6063 3.94417 0.020225 0 0 -3.09763 + 1 1 1 + + 10.6303 3.94523 -0.007775 0 0 -3.09763 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 10.6063 3.94417 0.020225 0 0 -3.09763 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 10.4976 3.86682 0.020225 1.5708 -0 -3.09763 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 10.4912 4.01168 0.020225 1.5708 -0 -3.09763 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 10.492 3.85806 0.020225 1.5708 -0 -3.09763 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 10.4848 4.0199 0.020225 1.5708 -0 -3.09763 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 10.7617 3.86993 0.020225 1.5708 -0 -3.09763 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 10.7546 4.03176 0.020225 1.5708 -0 -3.09763 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + + 10.7857 11.0385 0.016986 0 -0 2.63452 + 1 1 1 + + 10.8066 11.0269 -0.011014 0 -0 2.63452 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 10.7857 11.0385 0.016986 0 -0 2.63452 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 10.6525 11.0295 0.016986 1.5708 -0 2.63452 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 10.723 11.1562 0.016986 1.5708 -0 2.63452 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 10.6432 11.025 0.016986 1.5708 -0 2.63452 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 10.7219 11.1666 0.016986 1.5708 -0 2.63452 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 10.8793 10.8939 0.016986 1.5708 -0 2.63452 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 10.9579 11.0355 0.016986 1.5708 -0 2.63452 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + + 17.2486 2.43925 0.081437 0 -0 1.58468 + 1 1 1 + + 17.2479 2.43925 0.180377 0 0 -1.55692 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 17.2493 2.44145 0.005377 0 -0 1.58468 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + + 9.35645 4.27003 0.083977 0 -0 3.12651 + 1 1 1 + + 9.35644 4.26935 0.182917 0 -0 -0.015084 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 9.35427 4.27074 0.007917 0 -0 3.12651 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + + 8.21462 0.628029 0.083325 0 -0 0 + 1 1 1 + + 8.21462 0.628711 0.182265 0 -0 3.14159 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 8.21681 0.627347 0.007265 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + + 14.3093 10.9033 0.079672 0 -0 0 + 1 1 1 + + 14.3113 10.9033 0.178612 0 -0 3.14159 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 14.3115 10.9026 0.003612 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + + 6.23435 11.4669 0.07925 0 0 -3.12466 + 1 1 1 + + 6.23236 11.4668 0.17819 0 -0 0.016933 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 6.23215 11.4675 0.00319 0 -0 -3.12466 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + + 14.3213 9.99112 0.08133 0 -0 3.09003 + 1 1 1 + + 14.3213 9.99111 0.18027 0 0 -0.051558 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 14.3191 9.99191 0.00527 0 -0 3.09003 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + + 6.19341 12.4108 0.079015 0 -0 0 + 1 1 1 + + 6.1934 12.4108 0.177955 0 -0 3.14159 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 6.1956 12.4101 0.002955 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + + 5.63 4.292 0 0 -0 0 + 1 1 1 + + 5.58132 4.29416 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 5.61132 4.29128 0.09102 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 5.61132 4.29128 0.21022 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 5.61132 4.29128 0.15 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + + 2.23931 3.02431 0.083495 0 0 -1.56766 + 1 1 1 + + 2.23931 3.0243 0.182435 0 -0 1.57393 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 2.23864 3.02211 0.007435 0 0 -1.56766 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + + 8.84222 1.30756 0.081294 0 -0 0 + 1 1 1 + + 8.84003 1.30825 0.180234 0 -0 3.14159 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 8.84441 1.30688 0.005234 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + + 15.9246 6.2717 0.000294 0.004578 -3.1e-05 -1.53683 + 1 1 1 + + 15.9246 6.2717 0.000294 0.004578 -3.1e-05 -1.53683 + 0 0 0 0 -0 0 + 0 0 -9.8 0 -0 0 + 0 0 -9.8 0 -0 0 + + + + 7.46564 4.29209 0.079003 0 -0 3.00126 + 1 1 1 + + 7.4677 4.29111 0.177943 0 0 -0.140335 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 7.46356 4.29307 0.002943 0 -0 3.00126 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + + 5.52269 3.70126 0.085655 0 -0 0 + 1 1 1 + + 5.52269 3.70126 0.184595 0 -0 3.14159 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 5.52488 3.70058 0.009595 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + + 5.35811 5.93046 0.083267 0 0 -1.61375 + 1 1 1 + + 5.35812 5.93046 0.182207 0 -0 1.52784 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 5.35733 5.9283 0.007207 0 0 -1.61375 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + + 1.94744 7.09926 0.080167 0 -0 3.13954 + 1 1 1 + + 1.94745 7.09926 0.179107 0 -0 -0.002061 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 1.94525 7.09995 0.004107 0 -0 3.13954 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + + 4.44864 5.89547 0.082877 0 0 -1.61287 + 1 1 1 + + 4.44864 5.89547 0.181817 0 -0 1.52872 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 4.44787 5.89331 0.006817 0 0 -1.61287 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + + 3.40939 11.8506 0 0 -0 0 + 1 1 1 + + 3.40939 11.8506 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + + 16.5081 9.58533 0.079528 0 -0 1.50458 + 1 1 1 + + 16.5089 9.58747 0.003468 0 -0 1.50458 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 16.5073 9.58317 0.178468 0 0 -1.63702 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + + 2.23 4.54 0 0 -0 1.55 + 1 1 1 + + 2.22683 4.49137 0 0 -0 1.55 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 2.23033 4.52131 0.09102 0 -0 1.55 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 2.23033 4.52131 0.21022 0 -0 1.55 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 2.23033 4.52131 0.15 0 -0 1.55 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + + 4.17756 3.27541 0 0 0 -3.13 + 1 1 1 + + 4.22626 3.27382 -0 0 0 -3.13 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 4.19623 3.27635 0.09102 0 0 -3.13 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 4.19623 3.27635 0.21022 0 0 -3.13 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 4.19623 3.27635 0.15 0 0 -3.13 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + + 16.1539 4.28285 0.085966 0 -0 2.82635 + 1 1 1 + + 16.1557 4.28152 0.184906 0 -0 -0.315238 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 16.152 4.28418 0.009906 0 -0 2.82635 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + + 0.106598 10.4435 0.080563 0 0 -1.60745 + 1 1 1 + + 0.107359 10.4457 0.179503 0 -0 1.53415 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 0.105836 10.4413 0.004503 0 0 -1.60745 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + + 3.38193 7.12205 0.075938 0 0 -3.14123 + 1 1 1 + + 3.38412 7.12136 0.174878 0 -0 0.000351 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 3.37974 7.12273 -0.000122 0 0 -3.14123 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + + 4.2687 0.126699 0.082327 0 -0 0 + 1 1 1 + + 4.26651 0.127381 0.181267 0 -0 3.14159 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 4.27089 0.126017 0.006267 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + + 15.4644 11.4924 0.078783 0 0 -1.59355 + 1 1 1 + + 15.4651 11.4946 0.177723 0 -0 1.54804 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + 15.4637 11.4902 0.002723 0 0 -1.59355 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + + 10.34 6.87 0 0 -0 0 + 1 1 1 + + 10.34 6.87 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + 0 0 0 0 -0 0 + + + + 1.27169 12.3576 2 0 -0 0 + + + 14.6784 13.1624 2 0 -0 0 + + + 0.402477 0.104987 2 0 -0 0 + + + 14.3677 -0.223562 2 0 -0 0 + + + + + 11.427 1.92738 6.04807 1.2e-05 1.5698 1.56408 + orbit + perspective + + + + 1 + + + 0.5 + + 0.166667 + 0 + 0 + 0.166667 + 0 + 0.166667 + + 0 0 -0.078998 0 -0 0 + 0 0 0 0 -0 0 + + 0.002193 -0.000682 -0.07606 0 -0 0 + 1 + 1 + 0 + 1 + + 0 0 0 0 -0 0 + + + model://subassembly/meshes/sign_leg.dae + 1 1 1 + + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + model://subassembly/meshes/sign_leg.dae + 1 1 1 + + + + + + 0.46 + 1 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+13 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+13 + 1 + + + + + + + + 0.022 + + 0.166667 + 0 + 0 + 0.166667 + 0 + 0.166667 + + 0 0 0 0 -0 0 + + -0 0.000682 0.09894 0 -0 3.14159 + 1 + 1 + 0 + 1 + + 0 0 0 0 -0 0 + + + model://crosswalk_sign/meshes/pedestrian_sign.dae + 10 10 10 + + + + 1 + + + __default__ + + 0.3 0.3 0.3 1 + 0.7 0.7 0.7 1 + 0.01 0.01 0.01 1 + 0 0 0 1 + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + model://crosswalk_sign/meshes/pedestrian_sign.dae + 10 10 10 + + + + + + 0.46 + 1 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+13 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+13 + 1 + + + + + + + link_0 + link_1 + 0 0 0 0 -0 0 + + + + 0 + 0.2 + + + 0 + 0.2 + + + + + 1 + 1 + 17.3282 2.40199 0 0 -0 0 + + + 1 + + + 0.5 + + 0.166667 + 0 + 0 + 0.166667 + 0 + 0.166667 + + 0 0 -0.078998 0 -0 0 + 0 0 0 0 -0 0 + + 0.002193 -0.000682 -0.07606 0 -0 0 + 1 + 1 + 0 + 1 + + 0 0 0 0 -0 0 + + + model://subassembly/meshes/sign_leg.dae + 1 1 1 + + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + model://subassembly/meshes/sign_leg.dae + 1 1 1 + + + + + + 0.46 + 1 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+13 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+13 + 1 + + + + + + + + 0.022 + + 0.166667 + 0 + 0 + 0.166667 + 0 + 0.166667 + + 0 0 0 0 -0 0 + + -0.002193 0.000682 0.09894 0 -0 3.14159 + 1 + 1 + 0 + 1 + + 0 0 0 0 -0 0 + + + model://stop_sign/meshes/stop_sign.dae + 1 1 1 + + + + 1 + + + __default__ + + 0.3 0.3 0.3 1 + 0.7 0.7 0.7 1 + 0.01 0.01 0.01 1 + 0 0 0 1 + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + model://stop_sign/meshes/stop_sign.dae + 1 1 1 + + + + + + 0.46 + 1 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+13 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+13 + 1 + + + + + + + link_0 + link_1 + 0 0 0 0 -0 0 + + + + 0 + 0.2 + + + 0 + 0.2 + + + + + 1 + 1 + 16.1476 4.24982 0 0 -0 0 + + + 1 + + + 0.5 + + 0.166667 + 0 + 0 + 0.166667 + 0 + 0.166667 + + 0 0 -0.078998 0 -0 0 + 0 0 0 0 -0 0 + + 0.002193 -0.000682 -0.07606 0 -0 0 + 1 + 1 + 0 + 1 + + 0 0 0 0 -0 0 + + + model://subassembly/meshes/sign_leg.dae + 1 1 1 + + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + model://subassembly/meshes/sign_leg.dae + 1 1 1 + + + + + + 0.46 + 1 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+13 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+13 + 1 + + + + + + + + 0.022 + + 0.166667 + 0 + 0 + 0.166667 + 0 + 0.166667 + + 0 0 0 0 -0 0 + + -0.002193 0.000682 0.09894 0 -0 3.14159 + 1 + 1 + 0 + 1 + + 0 0 0 0 -0 0 + + + model://roundabout_sign/meshes/roundabout_sign.dae + 1 1 1 + + + + 1 + + + __default__ + + 0.3 0.3 0.3 1 + 0.7 0.7 0.7 1 + 0.01 0.01 0.01 1 + 0 0 0 1 + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + model://roundabout_sign/meshes/roundabout_sign.dae + 1 1 1 + + + + + + 0.46 + 1 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+13 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+13 + 1 + + + + + + + link_0 + link_1 + 0 0 0 0 -0 0 + + + + 0 + 0.2 + + + 0 + 0.2 + + + + + 1 + 1 + 16.4854 9.60945 0 0 -0 0 + + + 1 + + + 0.5 + + 0.166667 + 0 + 0 + 0.166667 + 0 + 0.166667 + + 0 0 -0.078998 0 -0 0 + 0 0 0 0 -0 0 + + 0.002193 -0.000682 -0.07606 0 -0 0 + 1 + 1 + 0 + 1 + + 0 0 0 0 -0 0 + + + model://subassembly/meshes/sign_leg.dae + 1 1 1 + + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + model://subassembly/meshes/sign_leg.dae + 1 1 1 + + + + + + 0.46 + 1 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+13 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+13 + 1 + + + + + + + + 0.022 + + 0.166667 + 0 + 0 + 0.166667 + 0 + 0.166667 + + 0 0 0 0 -0 0 + + 0.002 0 0.09894 0 -0 3.14159 + 1 + 1 + 0 + 1 + + 0 0 0 0 -0 0 + + + model://enter_highway_sign/meshes/enter_highway_sign.dae + 1 1 1 + + + + 1 + + + __default__ + + 0.3 0.3 0.3 1 + 0.7 0.7 0.7 1 + 0.01 0.01 0.01 1 + 0 0 0 1 + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + model://enter_highway_sign/meshes/enter_highway_sign.dae + 1 1 1 + + + + + + 0.46 + 1 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+13 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+13 + 1 + + + + + + + link_0 + link_1 + 0 0 0 0 -0 0 + + + + 0 + 0.2 + + + 0 + 0.2 + + + + + 1 + 1 + 14.3093 10.9738 0 0 -0 0 + + + 1 + + + 0.5 + + 0.166667 + 0 + 0 + 0.166667 + 0 + 0.166667 + + 0 0 -0.078998 0 -0 0 + 0 0 0 0 -0 0 + + 0.002193 -0.000682 -0.07606 0 -0 0 + 1 + 1 + 0 + 1 + + 0 0 0 0 -0 0 + + + model://subassembly/meshes/sign_leg.dae + 1 1 1 + + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + model://subassembly/meshes/sign_leg.dae + 1 1 1 + + + + + + 0.46 + 1 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+13 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+13 + 1 + + + + + + + + 0.022 + + 0.166667 + 0 + 0 + 0.166667 + 0 + 0.166667 + + 0 0 0 0 -0 0 + + -0 0 0.09894 0 -0 3.14159 + 1 + 1 + 0 + 1 + + -0.002 0 0 0 -0 0 + + + model://leave_highway_sign/meshes/leave_highway_sign.dae + 1 1 1 + + + + 1 + + + __default__ + + 0.3 0.3 0.3 1 + 0.7 0.7 0.7 1 + 0.01 0.01 0.01 1 + 0 0 0 1 + + 0 + 1 + + + 0 + 10 + -0.002 0 0 0 -0 0 + + + model://leave_highway_sign/meshes/leave_highway_sign.dae + 1 1 1 + + + + + + 0.46 + 1 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+13 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+13 + 1 + + + + + + + link_0 + link_1 + 0 0 0 0 -0 0 + + + + 0 + 0.2 + + + 0 + 0.2 + + + + + 1 + 1 + 14.3739 10.0612 0 0 -0 0 + + + 1 + + + 0.5 + + 0.166667 + 0 + 0 + 0.166667 + 0 + 0.166667 + + 0 0 -0.078998 0 -0 0 + 0 0 0 0 -0 0 + + 0.002193 -0.000682 -0.07606 0 -0 0 + 1 + 1 + 0 + 1 + + 0 0 0 0 -0 0 + + + model://subassembly/meshes/sign_leg.dae + 1 1 1 + + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + model://subassembly/meshes/sign_leg.dae + 1 1 1 + + + + + + 0.46 + 1 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+13 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+13 + 1 + + + + + + + + 0.022 + + 0.166667 + 0 + 0 + 0.166667 + 0 + 0.166667 + + 0 0 0 0 -0 0 + + -0 0 0.09894 0 -0 3.14159 + 1 + 1 + 0 + 1 + + -0.002 0 0 0 -0 0 + + + model://leave_highway_sign/meshes/leave_highway_sign.dae + 1 1 1 + + + + 1 + + + __default__ + + 0.3 0.3 0.3 1 + 0.7 0.7 0.7 1 + 0.01 0.01 0.01 1 + 0 0 0 1 + + 0 + 1 + + + 0 + 10 + -0.002 0 0 0 -0 0 + + + model://leave_highway_sign/meshes/leave_highway_sign.dae + 1 1 1 + + + + + + 0.46 + 1 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+13 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+13 + 1 + + + + + + + link_0 + link_1 + 0 0 0 0 -0 0 + + + + 0 + 0.2 + + + 0 + 0.2 + + + + + 1 + 1 + 6.19341 12.4108 0 0 -0 0 + + + 1 + + + 0.5 + + 0.166667 + 0 + 0 + 0.166667 + 0 + 0.166667 + + 0 0 -0.078998 0 -0 0 + 0 0 0 0 -0 0 + + 0.002193 -0.000682 -0.07606 0 -0 0 + 1 + 1 + 0 + 1 + + 0 0 0 0 -0 0 + + + model://subassembly/meshes/sign_leg.dae + 1 1 1 + + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + model://subassembly/meshes/sign_leg.dae + 1 1 1 + + + + + + 0.46 + 1 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+13 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+13 + 1 + + + + + + + + 0.022 + + 0.166667 + 0 + 0 + 0.166667 + 0 + 0.166667 + + 0 0 0 0 -0 0 + + 0.002 0 0.09894 0 -0 3.14159 + 1 + 1 + 0 + 1 + + 0 0 0 0 -0 0 + + + model://enter_highway_sign/meshes/enter_highway_sign.dae + 1 1 1 + + + + 1 + + + __default__ + + 0.3 0.3 0.3 1 + 0.7 0.7 0.7 1 + 0.01 0.01 0.01 1 + 0 0 0 1 + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + model://enter_highway_sign/meshes/enter_highway_sign.dae + 1 1 1 + + + + + + 0.46 + 1 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+13 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+13 + 1 + + + + + + + link_0 + link_1 + 0 0 0 0 -0 0 + + + + 0 + 0.2 + + + 0 + 0.2 + + + + + 1 + 1 + 6.24484 11.4323 0 0 -0 0 + + + + + 1 + + 0.166667 + 0 + 0 + 0.166667 + 0 + 0.166667 + + 0 0 0 0 -0 0 + + 0 0 0 0 -0 0 + 1 + 0 + 0 + 0 + + 0 0 0 0 -0 0 + + + model://ramp/meshes/ramp.dae + 1 1 1 + + + + 1 + 1 1 1 1 + 1 1 1 1 + 0 0 0 0 + 1 1 1 1 + + + __default__ + + 0.3 0.3 0.3 1 + 0.7 0.7 0.7 1 + 0.01 0.01 0.01 1 + 0 0 0 1 + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + model://ramp/meshes/ramp.dae + 1 1 1 + + + + + + 1 + 1 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+13 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+13 + 1 + + + + + + 1 + 1 + 2.97498 11.9247 0 0 -0 0 + + + 1 + + + 0.5 + + 0.166667 + 0 + 0 + 0.166667 + 0 + 0.166667 + + 0 0 -0.078998 0 -0 0 + 0 0 0 0 -0 0 + + 0.002193 -0.000682 -0.07606 0 -0 0 + 1 + 1 + 0 + 1 + + 0 0 0 0 -0 0 + + + model://subassembly/meshes/sign_leg.dae + 1 1 1 + + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + model://subassembly/meshes/sign_leg.dae + 1 1 1 + + + + + + 0.46 + 1 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+13 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+13 + 1 + + + + + + + + 0.022 + + 0.166667 + 0 + 0 + 0.166667 + 0 + 0.166667 + + 0 0 0 0 -0 0 + + -0.002193 0.000682 0.09894 0 -0 3.14159 + 1 + 1 + 0 + 1 + + 0 0 0 0 -0 0 + + + model://stop_sign/meshes/stop_sign.dae + 1 1 1 + + + + 1 + + + __default__ + + 0.3 0.3 0.3 1 + 0.7 0.7 0.7 1 + 0.01 0.01 0.01 1 + 0 0 0 1 + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + model://stop_sign/meshes/stop_sign.dae + 1 1 1 + + + + + + 0.46 + 1 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+13 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+13 + 1 + + + + + + + link_0 + link_1 + 0 0 0 0 -0 0 + + + + 0 + 0.2 + + + 0 + 0.2 + + + + + 1 + 1 + 0.145845 10.4301 0 0 -0 0 + + + 1 + + 0 0 0 0 -0 0 + + + + 0.26244 + + 0.00283444 + 0 + 0 + 0.00039867 + 0 + 0.00323293 + + 0 0 0 0 -0 0 + + 0 0 0 0 -0 0 + 1 + 0 + 0 + + 0 0 -0.0065 0 -0 0 + + + model://chassis/meshes/chassisV5.dae + 1 1 1 + + + + 1 + + __default__ + + 0.6 0.6 0.6 1 + 0.85 0.85 0.85 1 + 0.6 0.6 0.6 1 + 0 0 0 1 + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + 0.135 0.36 0.002 + + + + + + 1 + 1 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+13 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+13 + 1 + + + + + 0 + + + -0.152 0.081 0 0 -0 0 + + + + 0.038 + + 1.283e-05 + 0 + 0 + 1.283e-05 + 0 + 2.069e-05 + + 0 0 0 0 -0 0 + + 0 0 0 1.5708 -0 0 + 1 + 0 + 0 + + 0 0 0 -1.5708 0 0 + + + model://wheel_left/meshes/rim.dae + 1 1 1 + + + + 1 + + __default__ + + 0.2 0.2 0.2 1 + 0.3 0.3 0.3 1 + 0.5 0.5 0.5 1 + 0 0 0 0 + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + 0.033 + 0.028 + + + + + + 0.7 + 0.7 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+07 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+07 + 1 + + + + + 0 + + + -0.152 -0.081 0 0 -0 0 + + + + 0.038 + + 1.283e-05 + 0 + 0 + 1.283e-05 + 0 + 2.069e-05 + + 0 0 0 0 -0 0 + + 0 0 0 1.5708 -0 0 + 1 + 0 + 0 + + 0 0 0 -1.5708 0 0 + + + model://wheel_right/meshes/rim.dae + 1 1 1 + + + + 1 + + __default__ + + 0.2 0.2 0.2 1 + 0.3 0.3 0.3 1 + 0.5 0.5 0.5 1 + 0 0 0 0 + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + 0.033 + 0.028 + + + + + + 0.7 + 0.7 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+07 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+07 + 1 + + + + + 0 + + + 0.112 0.0725 0 0 -0 0 + + + + 0.001 + + 0.00014583 + 0 + 0 + 0.00014583 + 0 + 0.000125 + + 0 0 0 0 -0 0 + + 0 0 0 1.5708 -0 0 + 1 + 0 + 0 + 0 + + 0 0 0 0 -0 0 + + + 0.001 + 0.01 + + + + 1 + + + __default__ + + 0.3 0.3 0.3 1 + 0.7 0.7 0.7 1 + 0.01 0.01 0.01 1 + 0 0 0 1 + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + 0.001 + 0.01 + + + + + + 1 + 1 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+13 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+13 + 1 + + + + + + + 0.112 -0.0725 0 0 -0 0 + + + + 0.001 + + 0.00014583 + 0 + 0 + 0.00014583 + 0 + 0.000125 + + 0 0 0 0 -0 0 + + 0 0 0 1.5708 -0 0 + 1 + 0 + 0 + 0 + + 0 0 0 0 -0 0 + + + 0.001 + 0.01 + + + + 1 + + + __default__ + + 0.3 0.3 0.3 1 + 0.7 0.7 0.7 1 + 0.01 0.01 0.01 1 + 0 0 0 1 + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + 0.001 + 0.01 + + + + + + 1 + 1 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+13 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+13 + 1 + + + + + + + 0.118 0.081 0 0 -0 0 + + + + 0.038 + + 1.283e-05 + 0 + 0 + 1.283e-05 + 0 + 2.069e-05 + + 0 0 0 0 -0 0 + + 0 0 0 1.5708 -0 0 + 1 + 0 + 0 + + 0 0 0 -1.5708 0 0 + + + model://wheel_left/meshes/rim.dae + 1 1 1 + + + + 1 + + __default__ + + 0.2 0.2 0.2 1 + 0.3 0.3 0.3 1 + 0.5 0.5 0.5 1 + 0 0 0 0 + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + 0.033 + 0.028 + + + + + + 0.7 + 0.7 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+07 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+07 + 1 + + + + + 0 + + + 0.118 -0.081 0 0 -0 0 + + + + 0.038 + + 1.283e-05 + 0 + 0 + 1.283e-05 + 0 + 2.069e-05 + + 0 0 0 0 -0 0 + + 0 0 0 1.5708 -0 0 + 1 + 0 + 0 + + 0 0 0 -1.5708 0 0 + + + model://wheel_right/meshes/rim.dae + 1 1 1 + + + + 1 + + __default__ + + 0.2 0.2 0.2 1 + 0.3 0.3 0.3 1 + 0.5 0.5 0.5 1 + 0 0 0 0 + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + 0.033 + 0.028 + + + + + + 0.7 + 0.7 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+07 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+07 + 1 + + + + + 0 + + + -0.024 0 -0.028 0 -0 0 + + + + 1 + + 0.166667 + 0 + 0 + 0.166667 + 0 + 0.166667 + + + 0 -0 0 0 -0 0 + + 0 0 0 0 -0 0 + + + model://car_body/meshes/CarBody.dae + 1 1 1 + + + + + + 0 + 1 + + 0 + 0 + 0 + + 0 + 1 + + chassis::link + wheel_rear_left::link_rim + 0 0 0 0 -0 0 + + + chassis::link + wheel_rear_right::link_rim + 0 0 0 0 -0 0 + + + chassis::link + wheel_front_right::link_rim + 0 0 0 0 -0 0 + + + chassis::link + wheel_front_left::link_rim + 0 0 0 0 -0 0 + + + chassis::link + car_body::link + 0 0 0 0 -0 0 + + 10.6016 3.9712 0 0 -0 0 + + + 1 + + + 0.5 + + 0.166667 + 0 + 0 + 0.166667 + 0 + 0.166667 + + 0 0 -0.078998 0 -0 0 + 0 0 0 0 -0 0 + + 0.002193 -0.000682 -0.07606 0 -0 0 + 1 + 1 + 0 + 1 + + 0 0 0 0 -0 0 + + + model://subassembly/meshes/sign_leg.dae + 1 1 1 + + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + model://subassembly/meshes/sign_leg.dae + 1 1 1 + + + + + + 0.46 + 1 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+13 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+13 + 1 + + + + + + + + 0.022 + + 0.166667 + 0 + 0 + 0.166667 + 0 + 0.166667 + + 0 0 0 0 -0 0 + + -0 0.000682 0.09894 0 -0 3.14159 + 1 + 1 + 0 + 1 + + 0 0 0 0 -0 0 + + + model://crosswalk_sign/meshes/pedestrian_sign.dae + 10 10 10 + + + + 1 + + + __default__ + + 0.3 0.3 0.3 1 + 0.7 0.7 0.7 1 + 0.01 0.01 0.01 1 + 0 0 0 1 + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + model://crosswalk_sign/meshes/pedestrian_sign.dae + 10 10 10 + + + + + + 0.46 + 1 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+13 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+13 + 1 + + + + + + + link_0 + link_1 + 0 0 0 0 -0 0 + + + + 0 + 0.2 + + + 0 + 0.2 + + + + + 1 + 1 + 9.35645 4.27003 0 0 -0 0 + + + 1 + + + 0.5 + + 0.166667 + 0 + 0 + 0.166667 + 0 + 0.166667 + + 0 0 -0.078998 0 -0 0 + 0 0 0 0 -0 0 + + 0.002193 -0.000682 -0.07606 0 -0 0 + 1 + 1 + 0 + 1 + + 0 0 0 0 -0 0 + + + model://subassembly/meshes/sign_leg.dae + 1 1 1 + + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + model://subassembly/meshes/sign_leg.dae + 1 1 1 + + + + + + 0.46 + 1 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+13 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+13 + 1 + + + + + + + + 0.022 + + 0.166667 + 0 + 0 + 0.166667 + 0 + 0.166667 + + 0 0 0 0 -0 0 + + -0.002193 0.000682 0.09894 0 -0 3.14159 + 1 + 1 + 0 + 1 + + 0 0 0 0 -0 0 + + + model://priority_sign/meshes/priority_sign.dae + 1 1 1 + + + + 1 + + + __default__ + + 0.3 0.3 0.3 1 + 0.7 0.7 0.7 1 + 0.01 0.01 0.01 1 + 0 0 0 1 + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + model://priority_sign/meshes/priority_sign.dae + 1 1 1 + + + + + + 0.46 + 1 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+13 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+13 + 1 + + + + + + + link_0 + link_1 + 0 0 0 0 -0 0 + + + + 0 + 0.2 + + + 0 + 0.2 + + + + + 1 + 1 + 7.46197 4.26641 0 0 -0 0 + + + 1 + + + 0.5 + + 0.166667 + 0 + 0 + 0.166667 + 0 + 0.166667 + + 0 0 -0.078998 0 -0 0 + 0 0 0 0 -0 0 + + 0.002193 -0.000682 -0.07606 0 -0 0 + 1 + 1 + 0 + 1 + + 0 0 0 0 -0 0 + + + model://subassembly/meshes/sign_leg.dae + 1 1 1 + + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + model://subassembly/meshes/sign_leg.dae + 1 1 1 + + + + + + 0.46 + 1 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+13 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+13 + 1 + + + + + + + + 0.022 + + 0.166667 + 0 + 0 + 0.166667 + 0 + 0.166667 + + 0 0 0 0 -0 0 + + -0 0 0.09894 0 -0 3.14159 + 1 + 1 + 0 + 1 + + 0 0 0 0 -0 0 + + + model://prohibited_sign/meshes/notallowed_sign.dae + 1 1 1 + + + + 1 + + + __default__ + + 0.3 0.3 0.3 1 + 0.7 0.7 0.7 1 + 0.01 0.01 0.01 1 + 0 0 0 1 + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + model://prohibited_sign/meshes/notallowed_sign.dae + 1 1 1 + + + + + + 0.46 + 1 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+13 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+13 + 1 + + + + + + + link_0 + link_1 + 0 0 0 0 -0 0 + + + + 0 + 0.2 + + + 0 + 0.2 + + + + + 1 + 1 + 5.39002 5.9591 0 0 -0 0 + + + 1 + + + 0.5 + + 0.166667 + 0 + 0 + 0.166667 + 0 + 0.166667 + + 0 0 -0.078998 0 -0 0 + 0 0 0 0 -0 0 + + 0.002193 -0.000682 -0.07606 0 -0 0 + 1 + 1 + 0 + 1 + + 0 0 0 0 -0 0 + + + model://subassembly/meshes/sign_leg.dae + 1 1 1 + + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + model://subassembly/meshes/sign_leg.dae + 1 1 1 + + + + + + 0.46 + 1 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+13 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+13 + 1 + + + + + + + + 0.022 + + 0.166667 + 0 + 0 + 0.166667 + 0 + 0.166667 + + 0 0 0 0 -0 0 + + -0.002193 0.000682 0.09894 0 -0 3.14159 + 1 + 1 + 0 + 1 + + 0 0 0 0 -0 0 + + + model://stop_sign/meshes/stop_sign.dae + 1 1 1 + + + + 1 + + + __default__ + + 0.3 0.3 0.3 1 + 0.7 0.7 0.7 1 + 0.01 0.01 0.01 1 + 0 0 0 1 + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + model://stop_sign/meshes/stop_sign.dae + 1 1 1 + + + + + + 0.46 + 1 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+13 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+13 + 1 + + + + + + + link_0 + link_1 + 0 0 0 0 -0 0 + + + + 0 + 0.2 + + + 0 + 0.2 + + + + + 1 + 1 + 3.38193 7.12205 0 0 -0 0 + + + 1 + + + 0.5 + + 0.166667 + 0 + 0 + 0.166667 + 0 + 0.166667 + + 0 0 -0.078998 0 -0 0 + 0 0 0 0 -0 0 + + 0.002193 -0.000682 -0.07606 0 -0 0 + 1 + 1 + 0 + 1 + + 0 0 0 0 -0 0 + + + model://subassembly/meshes/sign_leg.dae + 1 1 1 + + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + model://subassembly/meshes/sign_leg.dae + 1 1 1 + + + + + + 0.46 + 1 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+13 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+13 + 1 + + + + + + + + 0.022 + + 0.166667 + 0 + 0 + 0.166667 + 0 + 0.166667 + + 0 0 0 0 -0 0 + + -0 0 0.09894 0 -0 3.14159 + 1 + 1 + 0 + 1 + + 0 0 0 0 -0 0 + + + model://prohibited_sign/meshes/notallowed_sign.dae + 1 1 1 + + + + 1 + + + __default__ + + 0.3 0.3 0.3 1 + 0.7 0.7 0.7 1 + 0.01 0.01 0.01 1 + 0 0 0 1 + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + model://prohibited_sign/meshes/notallowed_sign.dae + 1 1 1 + + + + + + 0.46 + 1 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+13 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+13 + 1 + + + + + + + link_0 + link_1 + 0 0 0 0 -0 0 + + + + 0 + 0.2 + + + 0 + 0.2 + + + + + 1 + 1 + 1.94744 7.09926 0 0 -0 0 + + + 1 + + + 0.5 + + 0.166667 + 0 + 0 + 0.166667 + 0 + 0.166667 + + 0 0 -0.078998 0 -0 0 + 0 0 0 0 -0 0 + + 0.002193 -0.000682 -0.07606 0 -0 0 + 1 + 1 + 0 + 1 + + 0 0 0 0 -0 0 + + + model://subassembly/meshes/sign_leg.dae + 1 1 1 + + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + model://subassembly/meshes/sign_leg.dae + 1 1 1 + + + + + + 0.46 + 1 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+13 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+13 + 1 + + + + + + + + 0.022 + + 0.166667 + 0 + 0 + 0.166667 + 0 + 0.166667 + + 0 0 0 0 -0 0 + + -0 0 0.09894 0 -0 3.14159 + 1 + 1 + 0 + 1 + + 0 0 0 0 -0 0 + + + model://oneway_sign/meshes/oneway_sign.dae + 10 10 10 + + + + 1 + + + __default__ + + 0.3 0.3 0.3 1 + 0.7 0.7 0.7 1 + 0.01 0.01 0.01 1 + 0 0 0 1 + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + model://oneway_sign/meshes/oneway_sign.dae + 10 10 10 + + + + + + 0.46 + 1 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+13 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+13 + 1 + + + + + + + link_0 + link_1 + 0 0 0 0 -0 0 + + + + 0 + 0.2 + + + 0 + 0.2 + + + + + 1 + 1 + 2.28675 3.02572 0 0 -0 0 + + + 1 + + + 0.5 + + 0.166667 + 0 + 0 + 0.166667 + 0 + 0.166667 + + 0 0 -0.078998 0 -0 0 + 0 0 0 0 -0 0 + + 0.002193 -0.000682 -0.07606 0 -0 0 + 1 + 1 + 0 + 1 + + 0 0 0 0 -0 0 + + + model://subassembly/meshes/sign_leg.dae + 1 1 1 + + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + model://subassembly/meshes/sign_leg.dae + 1 1 1 + + + + + + 0.46 + 1 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+13 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+13 + 1 + + + + + + + + 0.022 + + 0.166667 + 0 + 0 + 0.166667 + 0 + 0.166667 + + 0 0 0 0 -0 0 + + -0.002193 0.000682 0.09894 0 -0 3.14159 + 1 + 1 + 0 + 1 + + 0 0 0 0 -0 0 + + + model://stop_sign/meshes/stop_sign.dae + 1 1 1 + + + + 1 + + + __default__ + + 0.3 0.3 0.3 1 + 0.7 0.7 0.7 1 + 0.01 0.01 0.01 1 + 0 0 0 1 + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + model://stop_sign/meshes/stop_sign.dae + 1 1 1 + + + + + + 0.46 + 1 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+13 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+13 + 1 + + + + + + + link_0 + link_1 + 0 0 0 0 -0 0 + + + + 0 + 0.2 + + + 0 + 0.2 + + + + + 1 + 1 + 4.2687 0.452219 0 0 -0 0 + + + 1 + + + 0.5 + + 0.166667 + 0 + 0 + 0.166667 + 0 + 0.166667 + + 0 0 -0.078998 0 -0 0 + 0 0 0 0 -0 0 + + 0.002193 -0.000682 -0.07606 0 -0 0 + 1 + 1 + 0 + 1 + + 0 0 0 0 -0 0 + + + model://subassembly/meshes/sign_leg.dae + 1 1 1 + + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + model://subassembly/meshes/sign_leg.dae + 1 1 1 + + + + + + 0.46 + 1 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+13 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+13 + 1 + + + + + + + + 0.022 + + 0.166667 + 0 + 0 + 0.166667 + 0 + 0.166667 + + 0 0 0 0 -0 0 + + -0 0.000682 0.09894 0 -0 3.14159 + 1 + 1 + 0 + 1 + + 0 0 0 0 -0 0 + + + model://crosswalk_sign/meshes/pedestrian_sign.dae + 10 10 10 + + + + 1 + + + __default__ + + 0.3 0.3 0.3 1 + 0.7 0.7 0.7 1 + 0.01 0.01 0.01 1 + 0 0 0 1 + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + model://crosswalk_sign/meshes/pedestrian_sign.dae + 10 10 10 + + + + + + 0.46 + 1 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+13 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+13 + 1 + + + + + + + link_0 + link_1 + 0 0 0 0 -0 0 + + + + 0 + 0.2 + + + 0 + 0.2 + + + + + 1 + 1 + 8.21462 0.962849 0 0 -0 0 + + + 1 + + + 0.5 + + 0.166667 + 0 + 0 + 0.166667 + 0 + 0.166667 + + 0 0 -0.078998 0 -0 0 + 0 0 0 0 -0 0 + + 0.002193 -0.000682 -0.07606 0 -0 0 + 1 + 1 + 0 + 1 + + 0 0 0 0 -0 0 + + + model://subassembly/meshes/sign_leg.dae + 1 1 1 + + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + model://subassembly/meshes/sign_leg.dae + 1 1 1 + + + + + + 0.46 + 1 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+13 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+13 + 1 + + + + + + + + 0.022 + + 0.166667 + 0 + 0 + 0.166667 + 0 + 0.166667 + + 0 0 0 0 -0 0 + + -0.002193 0.000682 0.09894 0 -0 3.14159 + 1 + 1 + 0 + 1 + + 0 0 0 0 -0 0 + + + model://parking_sign/meshes/parking_sign.dae + 10 10 10 + + + + 1 + + + __default__ + + 0.3 0.3 0.3 1 + 0.7 0.7 0.7 1 + 0.01 0.01 0.01 1 + 0 0 0 1 + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + model://parking_sign/meshes/parking_sign.dae + 10 10 10 + + + + + + 0.46 + 1 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+13 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+13 + 1 + + + + + + + link_0 + link_1 + 0 0 0 0 -0 0 + + + + 0 + 0.2 + + + 0 + 0.2 + + + + + 1 + 1 + 8.9769 0.403798 0 0 -0 0 + + + + + 0.5 + + 0.166667 + 0 + 0 + 0 + 0 + 0 + + 0 0 0 0 -0 0 + + -0.048681 0.002156 0 0 -0 0 + 1 + 0 + 0 + 0 + + 0 0 0 0 -0 0 + + + model://traffic_light/meshes/traffic_light_case.dae + 1 1 1 + + + + + + __default__ + + 0.2 0.2 0.2 1 + 0.2 0.2 0.2 1 + 0.01 0.01 0.01 1 + 0 0 0 1 + + 0 + 0 + + + 0 + 10 + 0 0 0 0 -0 0 + + + model://traffic_light/meshes/traffic_light_case.dae + 1 1 1 + + + + + + 1 + 1 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+13 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+13 + 1 + + + + + + + + 4 0.001819 1 0 1.32589 0 + 0 1 0 0 + 0 1 0 0 + + 5 + 0 + 0.08 + 0 + + + 0 + 0.01 + 1 + + 0 + 0 0 -1 + + + 1 + + 0.166667 + 0 + 0 + 0.166667 + 0 + 0.166667 + + + -0.018681 -0.000719 0.09102 0 -0 0 + + 0 0 0 0 -0 0 + + + model://traffic_light/meshes/traffic_light_lens.dae + 1 1 1 + + + + + 0 0 0 1 + + 0 + 0 + + 0 + 0 + 0 + + + + 4 0.001819 1 0 1.32589 0 + 1 1 0 0 + 1 1 0 0 + + 5 + 0 + 0.08 + 0 + + + 0 + 0.01 + 1 + + 0 + 0 0 -1 + + + 1 + + 0.166667 + 0 + 0 + 0.166667 + 0 + 0.166667 + + + -0.018681 -0.000719 0.15 0 -0 0 + + 0 0 0 0 -0 0 + + + model://traffic_light/meshes/traffic_light_lens.dae + 1 1 1 + + + + + 0 0 0 1 + + 0 + 0 + + 0 + 0 + 0 + + + + 4 0.001819 1 0 1.32589 0 + 1 0 0 0 + 1 0 0 0 + + 5 + 0 + 0.08 + 0 + + + 0 + 0.01 + 1 + + 0 + 0 0 -1 + + + 1 + + 0.166667 + 0 + 0 + 0.166667 + 0 + 0.166667 + + + -0.018681 -0.000719 0.21022 0 -0 0 + + 0 0 0 0 -0 0 + + + model://traffic_light/meshes/traffic_light_lens.dae + 1 1 1 + + + + + 0 0 0 1 + + 0 + 0 + + 0 + 0 + 0 + + + case + red_lens + 0 0 0 0 -0 0 + + + + 0 + 0.2 + + + 0 + 0.2 + + + + + + case + yellow_lens + 0 0 0 0 -0 0 + + + + 0 + 0.2 + + + 0 + 0.2 + + + + + + case + green_lens + 0 0 0 0 -0 0 + + + + 0 + 0.2 + + + 0 + 0.2 + + + + + 1 + 1 + + / + + 4.21 3.303 0 0 0 -3.13 + + + + + 0.5 + + 0.166667 + 0 + 0 + 0 + 0 + 0 + + 0 0 0 0 -0 0 + + -0.048681 0.002156 0 0 -0 0 + 1 + 0 + 0 + 0 + + 0 0 0 0 -0 0 + + + model://traffic_light/meshes/traffic_light_case.dae + 1 1 1 + + + + + + __default__ + + 0.2 0.2 0.2 1 + 0.2 0.2 0.2 1 + 0.01 0.01 0.01 1 + 0 0 0 1 + + 0 + 0 + + + 0 + 10 + 0 0 0 0 -0 0 + + + model://traffic_light/meshes/traffic_light_case.dae + 1 1 1 + + + + + + 1 + 1 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+13 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+13 + 1 + + + + + + + + 4 0.001819 1 0 1.32589 0 + 0 1 0 0 + 0 1 0 0 + + 5 + 0 + 0.08 + 0 + + + 0 + 0.01 + 1 + + 0 + 0 0 -1 + + + 1 + + 0.166667 + 0 + 0 + 0.166667 + 0 + 0.166667 + + + -0.018681 -0.000719 0.09102 0 -0 0 + + 0 0 0 0 -0 0 + + + model://traffic_light/meshes/traffic_light_lens.dae + 1 1 1 + + + + + 0 0 0 1 + + 0 + 0 + + 0 + 0 + 0 + + + + 4 0.001819 1 0 1.32589 0 + 1 1 0 0 + 1 1 0 0 + + 5 + 0 + 0.08 + 0 + + + 0 + 0.01 + 1 + + 0 + 0 0 -1 + + + 1 + + 0.166667 + 0 + 0 + 0.166667 + 0 + 0.166667 + + + -0.018681 -0.000719 0.15 0 -0 0 + + 0 0 0 0 -0 0 + + + model://traffic_light/meshes/traffic_light_lens.dae + 1 1 1 + + + + + 0 0 0 1 + + 0 + 0 + + 0 + 0 + 0 + + + + 4 0.001819 1 0 1.32589 0 + 1 0 0 0 + 1 0 0 0 + + 5 + 0 + 0.08 + 0 + + + 0 + 0.01 + 1 + + 0 + 0 0 -1 + + + 1 + + 0.166667 + 0 + 0 + 0.166667 + 0 + 0.166667 + + + -0.018681 -0.000719 0.21022 0 -0 0 + + 0 0 0 0 -0 0 + + + model://traffic_light/meshes/traffic_light_lens.dae + 1 1 1 + + + + + 0 0 0 1 + + 0 + 0 + + 0 + 0 + 0 + + + case + red_lens + 0 0 0 0 -0 0 + + + + 0 + 0.2 + + + 0 + 0.2 + + + + + + case + yellow_lens + 0 0 0 0 -0 0 + + + + 0 + 0.2 + + + 0 + 0.2 + + + + + + case + green_lens + 0 0 0 0 -0 0 + + + + 0 + 0.2 + + + 0 + 0.2 + + + + + 1 + 1 + + / + + 5.63 4.292 0 0 -0 0 + + + + + 0.5 + + 0.166667 + 0 + 0 + 0 + 0 + 0 + + 0 0 0 0 -0 0 + + -0.048681 0.002156 0 0 -0 0 + 1 + 0 + 0 + 0 + + 0 0 0 0 -0 0 + + + model://traffic_light/meshes/traffic_light_case.dae + 1 1 1 + + + + + + __default__ + + 0.2 0.2 0.2 1 + 0.2 0.2 0.2 1 + 0.01 0.01 0.01 1 + 0 0 0 1 + + 0 + 0 + + + 0 + 10 + 0 0 0 0 -0 0 + + + model://traffic_light/meshes/traffic_light_case.dae + 1 1 1 + + + + + + 1 + 1 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+13 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+13 + 1 + + + + + + + + 4 0.001819 1 0 1.32589 0 + 0 1 0 0 + 0 1 0 0 + + 5 + 0 + 0.08 + 0 + + + 0 + 0.01 + 1 + + 0 + 0 0 -1 + + + 1 + + 0.166667 + 0 + 0 + 0.166667 + 0 + 0.166667 + + + -0.018681 -0.000719 0.09102 0 -0 0 + + 0 0 0 0 -0 0 + + + model://traffic_light/meshes/traffic_light_lens.dae + 1 1 1 + + + + + 0 0 0 1 + + 0 + 0 + + 0 + 0 + 0 + + + + 4 0.001819 1 0 1.32589 0 + 1 1 0 0 + 1 1 0 0 + + 5 + 0 + 0.08 + 0 + + + 0 + 0.01 + 1 + + 0 + 0 0 -1 + + + 1 + + 0.166667 + 0 + 0 + 0.166667 + 0 + 0.166667 + + + -0.018681 -0.000719 0.15 0 -0 0 + + 0 0 0 0 -0 0 + + + model://traffic_light/meshes/traffic_light_lens.dae + 1 1 1 + + + + + 0 0 0 1 + + 0 + 0 + + 0 + 0 + 0 + + + + 4 0.001819 1 0 1.32589 0 + 1 0 0 0 + 1 0 0 0 + + 5 + 0 + 0.08 + 0 + + + 0 + 0.01 + 1 + + 0 + 0 0 -1 + + + 1 + + 0.166667 + 0 + 0 + 0.166667 + 0 + 0.166667 + + + -0.018681 -0.000719 0.21022 0 -0 0 + + 0 0 0 0 -0 0 + + + model://traffic_light/meshes/traffic_light_lens.dae + 1 1 1 + + + + + 0 0 0 1 + + 0 + 0 + + 0 + 0 + 0 + + + case + red_lens + 0 0 0 0 -0 0 + + + + 0 + 0.2 + + + 0 + 0.2 + + + + + + case + yellow_lens + 0 0 0 0 -0 0 + + + + 0 + 0.2 + + + 0 + 0.2 + + + + + + case + green_lens + 0 0 0 0 -0 0 + + + + 0 + 0.2 + + + 0 + 0.2 + + + + + 1 + 1 + + / + + 2.23 4.54 0 0 -0 1.55 + + + + + 1 + + 0.166667 + 0 + 0 + 0.166667 + 0 + 0.166667 + + 0 0 0 0 -0 0 + + 0 0 -0 0 -0 0 + 1 + 0 + 0 + 0 + + 0 0 0 0 -0 0 + + + model://pedestrian_object/meshes/pedestrian_object.dae + 1 1 1 + + + + 1 + + + __default__ + + 0.3 0.3 0.3 1 + 0.7 0.7 0.7 1 + 0.01 0.01 0.01 1 + 0 0 0 1 + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + model://pedestrian_object/meshes/pedestrian_object.dae + 1 1 1 + + + + + + 0.1 + 0.1 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+13 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+13 + 1 + + + + + + 0 + 1 + + automobile + + 15.9246 6.2717 0 0 -0 0 + + + 1 + + 0 0 0 0 -0 0 + + + + 0.26244 + + 0.00283444 + 0 + 0 + 0.00039867 + 0 + 0.00323293 + + 0 0 0 0 -0 0 + + 0 0 0 0 -0 0 + 1 + 0 + 0 + + 0 0 -0.0065 0 -0 0 + + + model://chassis/meshes/chassisV5.dae + 1 1 1 + + + + 1 + + __default__ + + 0.6 0.6 0.6 1 + 0.85 0.85 0.85 1 + 0.6 0.6 0.6 1 + 0 0 0 1 + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + 0.135 0.36 0.002 + + + + + + 1 + 1 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+13 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+13 + 1 + + + + + 0 + + + -0.152 0.081 0 0 -0 0 + + + + 0.038 + + 1.283e-05 + 0 + 0 + 1.283e-05 + 0 + 2.069e-05 + + 0 0 0 0 -0 0 + + 0 0 0 1.5708 -0 0 + 1 + 0 + 0 + + 0 0 0 -1.5708 0 0 + + + model://wheel_left/meshes/rim.dae + 1 1 1 + + + + 1 + + __default__ + + 0.2 0.2 0.2 1 + 0.3 0.3 0.3 1 + 0.5 0.5 0.5 1 + 0 0 0 0 + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + 0.033 + 0.028 + + + + + + 0.7 + 0.7 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+07 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+07 + 1 + + + + + 0 + + + -0.152 -0.081 0 0 -0 0 + + + + 0.038 + + 1.283e-05 + 0 + 0 + 1.283e-05 + 0 + 2.069e-05 + + 0 0 0 0 -0 0 + + 0 0 0 1.5708 -0 0 + 1 + 0 + 0 + + 0 0 0 -1.5708 0 0 + + + model://wheel_right/meshes/rim.dae + 1 1 1 + + + + 1 + + __default__ + + 0.2 0.2 0.2 1 + 0.3 0.3 0.3 1 + 0.5 0.5 0.5 1 + 0 0 0 0 + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + 0.033 + 0.028 + + + + + + 0.7 + 0.7 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+07 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+07 + 1 + + + + + 0 + + + 0.112 0.0725 0 0 -0 0 + + + + 0.001 + + 0.00014583 + 0 + 0 + 0.00014583 + 0 + 0.000125 + + 0 0 0 0 -0 0 + + 0 0 0 1.5708 -0 0 + 1 + 0 + 0 + 0 + + 0 0 0 0 -0 0 + + + 0.001 + 0.01 + + + + 1 + + + __default__ + + 0.3 0.3 0.3 1 + 0.7 0.7 0.7 1 + 0.01 0.01 0.01 1 + 0 0 0 1 + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + 0.001 + 0.01 + + + + + + 1 + 1 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+13 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+13 + 1 + + + + + + + 0.112 -0.0725 0 0 -0 0 + + + + 0.001 + + 0.00014583 + 0 + 0 + 0.00014583 + 0 + 0.000125 + + 0 0 0 0 -0 0 + + 0 0 0 1.5708 -0 0 + 1 + 0 + 0 + 0 + + 0 0 0 0 -0 0 + + + 0.001 + 0.01 + + + + 1 + + + __default__ + + 0.3 0.3 0.3 1 + 0.7 0.7 0.7 1 + 0.01 0.01 0.01 1 + 0 0 0 1 + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + 0.001 + 0.01 + + + + + + 1 + 1 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+13 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+13 + 1 + + + + + + + 0.118 0.081 0 0 -0 0 + + + + 0.038 + + 1.283e-05 + 0 + 0 + 1.283e-05 + 0 + 2.069e-05 + + 0 0 0 0 -0 0 + + 0 0 0 1.5708 -0 0 + 1 + 0 + 0 + + 0 0 0 -1.5708 0 0 + + + model://wheel_left/meshes/rim.dae + 1 1 1 + + + + 1 + + __default__ + + 0.2 0.2 0.2 1 + 0.3 0.3 0.3 1 + 0.5 0.5 0.5 1 + 0 0 0 0 + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + 0.033 + 0.028 + + + + + + 0.7 + 0.7 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+07 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+07 + 1 + + + + + 0 + + + 0.118 -0.081 0 0 -0 0 + + + + 0.038 + + 1.283e-05 + 0 + 0 + 1.283e-05 + 0 + 2.069e-05 + + 0 0 0 0 -0 0 + + 0 0 0 1.5708 -0 0 + 1 + 0 + 0 + + 0 0 0 -1.5708 0 0 + + + model://wheel_right/meshes/rim.dae + 1 1 1 + + + + 1 + + __default__ + + 0.2 0.2 0.2 1 + 0.3 0.3 0.3 1 + 0.5 0.5 0.5 1 + 0 0 0 0 + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + 0.033 + 0.028 + + + + + + 0.7 + 0.7 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+07 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+07 + 1 + + + + + 0 + + + -0.024 0 -0.028 0 -0 0 + + + + 1 + + 0.166667 + 0 + 0 + 0.166667 + 0 + 0.166667 + + + 0 -0 0 0 -0 0 + + 0 0 0 0 -0 0 + + + model://car_body/meshes/CarBody.dae + 1 1 1 + + + + + + 0 + 1 + + 0 + 0 + 0 + + 0 + 1 + + chassis::link + wheel_rear_left::link_rim + 0 0 0 0 -0 0 + + + chassis::link + wheel_rear_right::link_rim + 0 0 0 0 -0 0 + + + chassis::link + wheel_front_right::link_rim + 0 0 0 0 -0 0 + + + chassis::link + wheel_front_left::link_rim + 0 0 0 0 -0 0 + + + chassis::link + car_body::link + 0 0 0 0 -0 0 + + 10.5941 11.2466 0 0 -0 0 + + + 1 + + + 0.5 + + 0.166667 + 0 + 0 + 0.166667 + 0 + 0.166667 + + 0 0 -0.078998 0 -0 0 + 0 0 0 0 -0 0 + + 0.002193 -0.000682 -0.07606 0 -0 0 + 1 + 1 + 0 + 1 + + 0 0 0 0 -0 0 + + + model://subassembly/meshes/sign_leg.dae + 1 1 1 + + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + model://subassembly/meshes/sign_leg.dae + 1 1 1 + + + + + + 0.46 + 1 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+13 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+13 + 1 + + + + + + + + 0.022 + + 0.166667 + 0 + 0 + 0.166667 + 0 + 0.166667 + + 0 0 0 0 -0 0 + + -0 0 0.09894 0 -0 3.14159 + 1 + 1 + 0 + 1 + + 0 0 0 0 -0 0 + + + model://prohibited_sign/meshes/notallowed_sign.dae + 1 1 1 + + + + 1 + + + __default__ + + 0.3 0.3 0.3 1 + 0.7 0.7 0.7 1 + 0.01 0.01 0.01 1 + 0 0 0 1 + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + model://prohibited_sign/meshes/notallowed_sign.dae + 1 1 1 + + + + + + 0.46 + 1 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+13 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+13 + 1 + + + + + + + link_0 + link_1 + 0 0 0 0 -0 0 + + + + 0 + 0.2 + + + 0 + 0.2 + + + + + 1 + 1 + 4.5139 5.88479 0 0 -0 0 + + + 1 + + + 0.5 + + 0.166667 + 0 + 0 + 0.166667 + 0 + 0.166667 + + 0 0 -0.078998 0 -0 0 + 0 0 0 0 -0 0 + + 0.002193 -0.000682 -0.07606 0 -0 0 + 1 + 1 + 0 + 1 + + 0 0 0 0 -0 0 + + + model://subassembly/meshes/sign_leg.dae + 1 1 1 + + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + model://subassembly/meshes/sign_leg.dae + 1 1 1 + + + + + + 0.46 + 1 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+13 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+13 + 1 + + + + + + + + 0.022 + + 0.166667 + 0 + 0 + 0.166667 + 0 + 0.166667 + + 0 0 0 0 -0 0 + + -0 0 0.09894 0 -0 3.14159 + 1 + 1 + 0 + 1 + + 0 0 0 0 -0 0 + + + model://prohibited_sign/meshes/notallowed_sign.dae + 1 1 1 + + + + 1 + + + __default__ + + 0.3 0.3 0.3 1 + 0.7 0.7 0.7 1 + 0.01 0.01 0.01 1 + 0 0 0 1 + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + model://prohibited_sign/meshes/notallowed_sign.dae + 1 1 1 + + + + + + 0.46 + 1 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+13 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+13 + 1 + + + + + + + link_0 + link_1 + 0 0 0 0 -0 0 + + + + 0 + 0.2 + + + 0 + 0.2 + + + + + 1 + 1 + 4.49814 5.89339 0 0 -0 0 + + + 1 + + + 0.5 + + 0.166667 + 0 + 0 + 0.166667 + 0 + 0.166667 + + 0 0 -0.078998 0 -0 0 + 0 0 0 0 -0 0 + + 0.002193 -0.000682 -0.07606 0 -0 0 + 1 + 1 + 0 + 1 + + 0 0 0 0 -0 0 + + + model://subassembly/meshes/sign_leg.dae + 1 1 1 + + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + model://subassembly/meshes/sign_leg.dae + 1 1 1 + + + + + + 0.46 + 1 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+13 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+13 + 1 + + + + + + + + 0.022 + + 0.166667 + 0 + 0 + 0.166667 + 0 + 0.166667 + + 0 0 0 0 -0 0 + + -0.002193 0.000682 0.09894 0 -0 3.14159 + 1 + 1 + 0 + 1 + + 0 0 0 0 -0 0 + + + model://stop_sign/meshes/stop_sign.dae + 1 1 1 + + + + 1 + + + __default__ + + 0.3 0.3 0.3 1 + 0.7 0.7 0.7 1 + 0.01 0.01 0.01 1 + 0 0 0 1 + + 0 + 1 + + + 0 + 10 + 0 0 0 0 -0 0 + + + model://stop_sign/meshes/stop_sign.dae + 1 1 1 + + + + + + 0.46 + 1 + 0 0 0 + 0 + 0 + + + 1 + 0 + 0 + 1 + + 0 + + + + + 0 + 1e+06 + + + 0 + 1 + 1 + + 0 + 0.2 + 1e+13 + 1 + 0.01 + 0 + + + 1 + -0.01 + 0 + 0.2 + 1e+13 + 1 + + + + + + + link_0 + link_1 + 0 0 0 0 -0 0 + + + + 0 + 0.2 + + + 0 + 0.2 + + + + + 1 + 1 + 15.5908 11.4895 0 0 -0 0 + + + diff --git a/src/traffic_light_pkg/src/tl_talker.py b/src/traffic_light_pkg/src/tl_talker.py index 2f1ed09..bccd530 100644 --- a/src/traffic_light_pkg/src/tl_talker.py +++ b/src/traffic_light_pkg/src/tl_talker.py @@ -37,7 +37,7 @@ def __init__(self): self.TL_interval = 1 #Initialize the node - rospy.init_node('traffic_light_publisher_node', anonymous=True) + rospy.init_node('traffic_light_publisher_node', anonymous=False) self.trafficlights = [] #Create a new publisher, specify the topic name, type of message and queue size @@ -70,16 +70,16 @@ def run(self): self.Color.RED, self.Color.YELLOW, - self.Color.YELLOW, + # self.Color.YELLOW, self.Color.GREEN, self.Color.GREEN, - self.Color.GREEN, - self.Color.GREEN, - self.Color.GREEN, + # self.Color.GREEN, + # self.Color.GREEN, + # self.Color.GREEN, self.Color.YELLOW, - self.Color.YELLOW + # self.Color.YELLOW ] # Cycles of patterns @@ -102,7 +102,7 @@ def run(self): #Send State for the start semaphore self.sendState(3, self.mirrorLight(self.main_state)) - self.rate.sleep() #publish at 10hz + self.rate.sleep() if __name__ == '__main__': try: diff --git a/src/weights/besLevhaS.pt b/src/weights/besLevhaS.pt new file mode 100644 index 0000000..54ecd01 Binary files /dev/null and b/src/weights/besLevhaS.pt differ diff --git a/src/weights/bosch.pt b/src/weights/bosch.pt new file mode 100644 index 0000000..47ed99a Binary files /dev/null and b/src/weights/bosch.pt differ diff --git a/src/weights/boschM100Epoch.pt b/src/weights/boschM100Epoch.pt new file mode 100644 index 0000000..fb7bb8d Binary files /dev/null and b/src/weights/boschM100Epoch.pt differ diff --git a/src/weights/dortTabela.pt b/src/weights/dortTabela.pt new file mode 100644 index 0000000..e242997 Binary files /dev/null and b/src/weights/dortTabela.pt differ diff --git a/src/weights/tabelalarM200Epoch.pt b/src/weights/tabelalarM200Epoch.pt new file mode 100644 index 0000000..7f00a2f Binary files /dev/null and b/src/weights/tabelalarM200Epoch.pt differ diff --git a/src/weights/topluLevhaM100Epoch.pt b/src/weights/topluLevhaM100Epoch.pt new file mode 100644 index 0000000..ab9ab74 Binary files /dev/null and b/src/weights/topluLevhaM100Epoch.pt differ diff --git a/traffic_lights_pkg.gif b/traffic_lights_pkg.gif new file mode 100644 index 0000000..b98f410 Binary files /dev/null and b/traffic_lights_pkg.gif differ