├── db
└── .gitplaceholder
├── docs
├── about
│ └── document-build.md
├── design
│ ├── architecture.md
│ ├── .pages
│ └── index.md
├── how-to-guides
│ ├── .pages
│ └── index.md
├── support
│ ├── .pages
│ ├── index.md
│ └── docs-guide.md
├── tutorials
│ ├── .pages
│ ├── planning-simulation
│ │ ├── .pages
│ │ ├── docker-env.png
│ │ ├── add-v2x-rviz-1.png
│ │ ├── add-v2x-rviz-2.png
│ │ ├── add-v2x-rviz-3.png
│ │ ├── autoware_1_rviz.png
│ │ ├── tools-properties.png
│ │ ├── wireshark_awv2x.png
│ │ ├── autowarev2x_sender.png
│ │ ├── autowarev2x_receiver.png
│ │ ├── network-emulation.md
│ │ ├── rosbag-and-analysis.md
│ │ └── index.md
│ ├── index.md
│ └── actual-devices
│ │ └── index.md
├── installation
│ ├── .pages
│ ├── index.md
│ ├── source-installation.md
│ └── docker-installation.md
├── figs
│ ├── architecture.png
│ ├── exp-setup_v2.png
│ ├── high-level-image-v4.png
│ └── autowarev2x_architecture_v2.png
├── stylesheets
│ └── extra.css
├── .pages
└── index.md
├── .gitignore
├── setup.sh
├── config
└── autoware_v2x.param.yaml
├── launch
└── v2x.launch.xml
├── cyclonedds.xml
├── include
└── autoware_v2x
│ ├── security.hpp
│ ├── positioning.hpp
│ ├── time_trigger.hpp
│ ├── ethernet_device.hpp
│ ├── dcc_passthrough.hpp
│ ├── link_layer.hpp
│ ├── v2x_app.hpp
│ ├── raw_socket_link.hpp
│ ├── application.hpp
│ ├── v2x_node.hpp
│ ├── router_context.hpp
│ └── cpm_application.hpp
├── src
├── positioning.cpp
├── link_layer.cpp
├── time_trigger.cpp
├── dcc_passthrough.cpp
├── ethernet_device.cpp
├── application.cpp
├── raw_socket_link.cpp
├── security.cpp
├── router_context.cpp
├── v2x_app.cpp
├── v2x_node.cpp
└── cpm_application.cpp
├── package.xml
├── .github
└── workflows
│ └── deploy-docs.yaml
├── README.md
├── CMakeLists.txt
├── mkdocs.yaml
└── cmake
└── FindGeographicLib.cmake
/db/.gitplaceholder:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/about/document-build.md:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/design/architecture.md:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/design/.pages:
--------------------------------------------------------------------------------
1 | nav:
2 | - index.md
--------------------------------------------------------------------------------
/docs/how-to-guides/.pages:
--------------------------------------------------------------------------------
1 | nav:
2 | - index.md
--------------------------------------------------------------------------------
/docs/support/.pages:
--------------------------------------------------------------------------------
1 | nav:
2 | - index.md
3 | - docs-guide.md
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | db/*.db
2 | doxygen/html/
3 | site/
4 |
5 | .DS_Store
--------------------------------------------------------------------------------
/docs/support/index.md:
--------------------------------------------------------------------------------
1 | # Support
2 |
3 | - Contact: yuasabe[at]hongo.wide.ad.jp
--------------------------------------------------------------------------------
/docs/how-to-guides/index.md:
--------------------------------------------------------------------------------
1 | # How-to-guides
2 |
3 | !!! Warning
4 | Under Construction
--------------------------------------------------------------------------------
/docs/tutorials/.pages:
--------------------------------------------------------------------------------
1 | nav:
2 | - index.md
3 | - planning-simulation
4 | - actual-devices
--------------------------------------------------------------------------------
/setup.sh:
--------------------------------------------------------------------------------
1 | # export CYCLONEDDS_URI=file://$PWD/cyclonedds.xml
2 | export ROS_DOMAIN_ID="$AWID"
--------------------------------------------------------------------------------
/docs/installation/.pages:
--------------------------------------------------------------------------------
1 | nav:
2 | - index.md
3 | - docker-installation.md
4 | - source-installation.md
--------------------------------------------------------------------------------
/docs/figs/architecture.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tlab-wide/AutowareV2X/HEAD/docs/figs/architecture.png
--------------------------------------------------------------------------------
/docs/figs/exp-setup_v2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tlab-wide/AutowareV2X/HEAD/docs/figs/exp-setup_v2.png
--------------------------------------------------------------------------------
/docs/stylesheets/extra.css:
--------------------------------------------------------------------------------
1 | .md-grid {
2 | max-width: 1920px;
3 | }
4 |
5 | img[alt~=diagram] { width: 60%; }
--------------------------------------------------------------------------------
/config/autoware_v2x.param.yaml:
--------------------------------------------------------------------------------
1 | /**:
2 | ros__parameters:
3 | network_interface: "wlp4s0"
4 | is_sender: true
--------------------------------------------------------------------------------
/docs/figs/high-level-image-v4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tlab-wide/AutowareV2X/HEAD/docs/figs/high-level-image-v4.png
--------------------------------------------------------------------------------
/docs/tutorials/planning-simulation/.pages:
--------------------------------------------------------------------------------
1 | nav:
2 | - index.md
3 | - rosbag-and-analysis.md
4 | - network-emulation.md
--------------------------------------------------------------------------------
/docs/.pages:
--------------------------------------------------------------------------------
1 | nav:
2 | - Introduction: index.md
3 | - installation
4 | - tutorials
5 | - how-to-guides
6 | - design
7 | - support
--------------------------------------------------------------------------------
/docs/figs/autowarev2x_architecture_v2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tlab-wide/AutowareV2X/HEAD/docs/figs/autowarev2x_architecture_v2.png
--------------------------------------------------------------------------------
/docs/tutorials/planning-simulation/docker-env.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tlab-wide/AutowareV2X/HEAD/docs/tutorials/planning-simulation/docker-env.png
--------------------------------------------------------------------------------
/docs/tutorials/planning-simulation/add-v2x-rviz-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tlab-wide/AutowareV2X/HEAD/docs/tutorials/planning-simulation/add-v2x-rviz-1.png
--------------------------------------------------------------------------------
/docs/tutorials/planning-simulation/add-v2x-rviz-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tlab-wide/AutowareV2X/HEAD/docs/tutorials/planning-simulation/add-v2x-rviz-2.png
--------------------------------------------------------------------------------
/docs/tutorials/planning-simulation/add-v2x-rviz-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tlab-wide/AutowareV2X/HEAD/docs/tutorials/planning-simulation/add-v2x-rviz-3.png
--------------------------------------------------------------------------------
/docs/tutorials/planning-simulation/autoware_1_rviz.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tlab-wide/AutowareV2X/HEAD/docs/tutorials/planning-simulation/autoware_1_rviz.png
--------------------------------------------------------------------------------
/docs/tutorials/planning-simulation/tools-properties.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tlab-wide/AutowareV2X/HEAD/docs/tutorials/planning-simulation/tools-properties.png
--------------------------------------------------------------------------------
/docs/tutorials/planning-simulation/wireshark_awv2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tlab-wide/AutowareV2X/HEAD/docs/tutorials/planning-simulation/wireshark_awv2x.png
--------------------------------------------------------------------------------
/docs/tutorials/planning-simulation/autowarev2x_sender.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tlab-wide/AutowareV2X/HEAD/docs/tutorials/planning-simulation/autowarev2x_sender.png
--------------------------------------------------------------------------------
/docs/tutorials/planning-simulation/autowarev2x_receiver.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tlab-wide/AutowareV2X/HEAD/docs/tutorials/planning-simulation/autowarev2x_receiver.png
--------------------------------------------------------------------------------
/docs/support/docs-guide.md:
--------------------------------------------------------------------------------
1 | # How to generate Documentation
2 |
3 | ## mkdocs
4 |
5 | Run development server.
6 | ```
7 | mkdocs serve
8 | ```
9 |
10 | Deploy to gh-pages.
11 | ```
12 | mkdocs gh-deploy
13 | ```
14 |
15 |
16 | ## Doxygen
17 |
18 | ```
19 | doxygen Doxyfile
20 | ```
--------------------------------------------------------------------------------
/launch/v2x.launch.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/cyclonedds.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | eth0
6 |
7 |
8 |
--------------------------------------------------------------------------------
/docs/tutorials/index.md:
--------------------------------------------------------------------------------
1 | # Tutorials
2 |
3 | AutowareV2X can be first verified using [Autoware's Planning Simulator](https://autowarefoundation.github.io/autoware-documentation/main/tutorials/ad-hoc-simulation/planning-simulation/).
4 |
5 | - [Running Autoware in the Planning Simulator](./planning-simulation/index.md)
6 |
7 | Then, after you are familiar with how AutowareV2X works, you can use actual devices to test AutowareV2X.
8 |
9 | - [Running AutowareV2X on Actual Devices](./actual-devices/index.md)
--------------------------------------------------------------------------------
/include/autoware_v2x/security.hpp:
--------------------------------------------------------------------------------
1 | #ifndef SECURITY_HPP_FV13ZIYA
2 | #define SECURITY_HPP_FV13ZIYA
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 |
11 | std::unique_ptr
12 | create_security_entity(const vanetza::Runtime&, vanetza::PositionProvider&);
13 |
14 | #endif /* SECURITY_HPP_FV13ZIYA */
15 |
16 |
--------------------------------------------------------------------------------
/docs/tutorials/planning-simulation/network-emulation.md:
--------------------------------------------------------------------------------
1 | # Network Emulation
2 |
3 | The `tc` command can be used within the Docker container to emulate various network parameters.
4 |
5 | In `autoware_1` for example, use `tc` to add a delay of 100ms.
6 | ```
7 | sudo tc qdisc add dev eth0 root netem delay 100ms
8 | ```
9 |
10 | To remove delay, simply:
11 | ```
12 | sudo tc qdisc delete dev eth0 root netem delay 100ms
13 | ```
14 |
15 | To show all qdisc:
16 | ```
17 | sudo tc qdisc show
18 | ```
19 |
20 | Documentation about [tc-netem](https://man7.org/linux/man-pages/man8/tc-netem.8.html).
--------------------------------------------------------------------------------
/docs/installation/index.md:
--------------------------------------------------------------------------------
1 | # Installing AutowareV2X
2 |
3 | AutowareV2X is used as an add-on module to the open-source autonomous driving stack called [Autoware](https://autowarefoundation.github.io/autoware-documentation/main/). Therefore, in order to properly use AutowareV2X, Autoware must first be installed on the system.
4 |
5 | You can install AutowareV2X directly onto a PC through the [Source Installation](./source-installation.md) or use it as a Docker container through the [Docker Installation](./docker-installation.md).
6 |
7 | !!! Note
8 | In order to proceed with the [Tutorials](../tutorials/index.md), you will need to continue with the [Docker Installatioon](./docker-installation.md).
--------------------------------------------------------------------------------
/include/autoware_v2x/positioning.hpp:
--------------------------------------------------------------------------------
1 | #ifndef POSITIONING_HPP_VZRIW7PB
2 | #define POSITIONING_HPP_VZRIW7PB
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 |
12 | class PositioningException : public std::runtime_error
13 | {
14 | using std::runtime_error::runtime_error;
15 | };
16 |
17 | std::unique_ptr
18 | create_position_provider(boost::asio::io_service &, const vanetza::Runtime &);
19 |
20 | #endif /* POSITIONING_HPP_VZRIW7PB */
21 |
--------------------------------------------------------------------------------
/include/autoware_v2x/time_trigger.hpp:
--------------------------------------------------------------------------------
1 | #ifndef TIME_TRIGGER_HPP_XRPGDYXO
2 | #define TIME_TRIGGER_HPP_XRPGDYXO
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | class TimeTrigger
10 | {
11 | public:
12 | TimeTrigger(boost::asio::io_service &);
13 | vanetza::Runtime & runtime() { return runtime_; }
14 | void schedule();
15 |
16 | private:
17 | boost::posix_time::ptime now() const;
18 | void on_timeout(const boost::system::error_code &);
19 | void update_runtime();
20 |
21 | boost::asio::io_service & io_service_;
22 | boost::asio::deadline_timer timer_;
23 | vanetza::ManualRuntime runtime_;
24 | };
25 |
26 | #endif /* TIME_TRIGGER_HPP_XRPGDYXO */
27 |
--------------------------------------------------------------------------------
/include/autoware_v2x/ethernet_device.hpp:
--------------------------------------------------------------------------------
1 | #ifndef ETHERNET_DEVICE_HPP_NEVC5DAY
2 | #define ETHERNET_DEVICE_HPP_NEVC5DAY
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | class EthernetDevice
9 | {
10 | public:
11 | using protocol = boost::asio::generic::raw_protocol;
12 |
13 | EthernetDevice(const char* devname);
14 | EthernetDevice(const EthernetDevice&) = delete;
15 | EthernetDevice& operator=(const EthernetDevice&) = delete;
16 | ~EthernetDevice();
17 |
18 | protocol::endpoint endpoint(int family) const;
19 | vanetza::MacAddress address() const;
20 |
21 | private:
22 | int index() const;
23 |
24 | int local_socket_;
25 | std::string interface_name_;
26 | };
27 |
28 | #endif /* ETHERNET_DEVICE_HPP_NEVC5DAY */
29 |
30 |
--------------------------------------------------------------------------------
/src/positioning.cpp:
--------------------------------------------------------------------------------
1 | #include "autoware_v2x/positioning.hpp"
2 | #include
3 |
4 | using namespace vanetza;
5 | namespace po = boost::program_options;
6 |
7 | std::unique_ptr create_position_provider(boost::asio::io_service &io_service, const Runtime &runtime)
8 | {
9 | std::unique_ptr positioning;
10 |
11 | std::unique_ptr stored{new StoredPositionProvider()};
12 | PositionFix fix;
13 | fix.timestamp = runtime.now();
14 | fix.latitude = 10.0 * units::degree;
15 | fix.longitude = 10.0 * units::degree;
16 | fix.confidence.semi_major = 1.0 * units::si::meter;
17 | fix.confidence.semi_minor = fix.confidence.semi_major;
18 | stored->position_fix(fix);
19 | positioning = std::move(stored);
20 |
21 | return positioning;
22 | }
--------------------------------------------------------------------------------
/src/link_layer.cpp:
--------------------------------------------------------------------------------
1 | #include "autoware_v2x/link_layer.hpp"
2 | #include
3 | #include
4 | #include "autoware_v2x/raw_socket_link.hpp"
5 |
6 | std::unique_ptr create_link_layer(
7 | boost::asio::io_service & io_service, const EthernetDevice & device, const std::string & name)
8 | {
9 | std::unique_ptr link_layer;
10 |
11 | if (name == "ethernet") {
12 | boost::asio::generic::raw_protocol raw_protocol(
13 | AF_PACKET, vanetza::access::ethertype::GeoNetworking.net());
14 | boost::asio::generic::raw_protocol::socket raw_socket(io_service, raw_protocol);
15 | raw_socket.bind(device.endpoint(AF_PACKET));
16 | link_layer.reset(new RawSocketLink{std::move(raw_socket)});
17 | }
18 |
19 | // Removed Cohda and UDP Support
20 |
21 | return link_layer;
22 | }
23 |
--------------------------------------------------------------------------------
/include/autoware_v2x/dcc_passthrough.hpp:
--------------------------------------------------------------------------------
1 | #ifndef DCC_PASSTHROUGH_HPP_GSDFESAE
2 | #define DCC_PASSTHROUGH_HPP_GSDFESAE
3 |
4 | #include "autoware_v2x/time_trigger.hpp"
5 | #include
6 | #include
7 | #include
8 | #include
9 |
10 | class DccPassthrough : public vanetza::dcc::RequestInterface
11 | {
12 | public:
13 | DccPassthrough(vanetza::access::Interface&, TimeTrigger& trigger);
14 |
15 | void request(const vanetza::dcc::DataRequest& request, std::unique_ptr packet) override;
16 |
17 | void allow_packet_flow(bool allow);
18 | bool allow_packet_flow();
19 |
20 | private:
21 | vanetza::access::Interface& access_;
22 | TimeTrigger& trigger_;
23 | bool allow_packet_flow_ = true;
24 | };
25 |
26 | #endif /* DCC_PASSTHROUGH_HPP_GSDFESAE */
27 |
--------------------------------------------------------------------------------
/include/autoware_v2x/link_layer.hpp:
--------------------------------------------------------------------------------
1 | #ifndef LINK_LAYER_HPP_FGEK0QTH
2 | #define LINK_LAYER_HPP_FGEK0QTH
3 |
4 | #include "autoware_v2x/ethernet_device.hpp"
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 |
12 | class LinkLayerIndication
13 | {
14 | public:
15 | using IndicationCallback = std::function;
16 |
17 | virtual void indicate(IndicationCallback) = 0;
18 | virtual ~LinkLayerIndication() = default;
19 | };
20 |
21 | class LinkLayer : public vanetza::access::Interface, public LinkLayerIndication
22 | {
23 | };
24 |
25 | std::unique_ptr
26 | create_link_layer(boost::asio::io_service&, const EthernetDevice&, const std::string& name);
27 |
28 | #endif /* LINK_LAYER_HPP_FGEK0QTH */
29 |
30 |
--------------------------------------------------------------------------------
/docs/tutorials/planning-simulation/rosbag-and-analysis.md:
--------------------------------------------------------------------------------
1 | # Recording Rosbag and Tcpdump for Analysis
2 |
3 | ## Record both rosbag and tcpdump
4 |
5 | In `autoware_1`:
6 | ```
7 | docker exec -it autoware_1 bash
8 | cd ~/workspace/autoware_docker
9 | ros2 bag record -o test_sender_rosbag /perception/object_recognition/objects /tf
10 |
11 | sudo apt update
12 | sudo apt install tcpdump
13 | sudo tcpdump -i eth0 -w test_sender_tcpdump.pcap
14 | ```
15 |
16 | In `autoware_2`:
17 | ```
18 | docker exec -it autoware_2 bash
19 | cd ~/workspace/autoware_docker
20 | ros2 bag record -o test_receiver_rosbag /v2x/cpm/objects /tf
21 |
22 | sudo apt update
23 | sudo apt install tcpdump
24 | sudo tcpdump -i eth0 -w test_receiver_tcpdump.pcap
25 | ```
26 |
27 | ## Open PCAP file in Wireshark
28 |
29 | 
30 |
31 | ## Analyze in JupyteLab
32 |
33 | ### Plot the x,y coordinates of objects in test_sender_rosbag
34 |
35 | 1. Export the necessary topics of the Rosbag to a CSV file
--------------------------------------------------------------------------------
/package.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | autoware_v2x
5 | 0.0.0
6 | TODO: Package description
7 | yuasabe
8 | TODO: License declaration
9 |
10 | ament_cmake_auto
11 |
12 | autoware_auto_perception_msgs
13 | tf2_msgs
14 | tf2
15 | geometry_msgs
16 | rclcpp
17 | std_msgs
18 | rclcpp_components
19 | Vanetza
20 |
21 | ament_lint_auto
22 | ament_lint_common
23 |
24 |
25 | ament_cmake
26 |
27 |
28 |
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | # AutowareV2X Documentation
2 |
3 | ## About AutowareV2X
4 |
5 | AutowareV2X is an open-source module that can be added onto the newest [Autoware.universe](https://github.com/autowarefoundation/autoware) to enable V2X communication.
6 |
7 | It utilizes [Vanetza](https://github.com/riebl/vanetza) as the protocol suite for ETSI C-ITS standards.
8 |
9 | We have also provided a working example of a CPM application, where Collective Perception Messages can be used to exchange perception information in Autoware.
10 |
11 | 
12 |
13 | AutowareV2X can be used in various V2X scenarios across different radio access channels.
14 |
15 | 
16 |
17 | ## Getting started
18 |
19 | - [Installation](./installation/index.md) pages explain the installation steps of AutowareV2X and its prerequisites.
20 | - [Tutorials](./tutorials/index.md) pages provide several tutorials to follow after installation.
21 | - [Design](./design/index.md) pages explain the design concept and architecture of AutowareV2X.
22 | - [Support](./support/index.md) pages are the place to go if you need additional help.
--------------------------------------------------------------------------------
/.github/workflows/deploy-docs.yaml:
--------------------------------------------------------------------------------
1 | name: deploy-docs
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | paths:
8 | - mkdocs.yaml
9 | - "**/*.md"
10 | - "**/*.svg"
11 | - "**/*.png"
12 | - "**/*.jpg"
13 | pull_request_target:
14 | types:
15 | - opened
16 | - synchronize
17 | - labeled
18 | workflow_dispatch:
19 |
20 | jobs:
21 | prevent-no-label-execution:
22 | uses: autowarefoundation/autoware-github-actions/.github/workflows/prevent-no-label-execution.yaml@v1
23 | with:
24 | label: deploy-docs
25 |
26 | deploy-docs:
27 | needs: prevent-no-label-execution
28 | if: ${{ needs.prevent-no-label-execution.outputs.run == 'true' }}
29 | runs-on: ubuntu-latest
30 | steps:
31 | - name: Check out repository
32 | uses: actions/checkout@v3
33 | with:
34 | fetch-depth: 0
35 | ref: ${{ github.event.pull_request.head.sha }}
36 |
37 | - name: Deploy docs
38 | uses: autowarefoundation/autoware-github-actions/deploy-docs@v1
39 | with:
40 | token: ${{ secrets.GITHUB_TOKEN }}
41 | latest: ${{ github.event_name != 'pull_request_target' && github.ref_name == github.event.repository.default_branch }}
--------------------------------------------------------------------------------
/src/time_trigger.cpp:
--------------------------------------------------------------------------------
1 | #include "autoware_v2x/time_trigger.hpp"
2 | #include
3 | #include
4 | #include
5 |
6 | namespace asio = boost::asio;
7 | namespace posix_time = boost::posix_time;
8 | using namespace vanetza;
9 |
10 | TimeTrigger::TimeTrigger(asio::io_service & io_service)
11 | : io_service_(io_service), timer_(io_service), runtime_(Clock::at(now()))
12 | {
13 | std::cout << "Starting runtime at " << now() << "\n";
14 | schedule();
15 | }
16 |
17 | posix_time::ptime TimeTrigger::now() const { return posix_time::microsec_clock::universal_time(); }
18 |
19 | void TimeTrigger::schedule()
20 | {
21 | update_runtime();
22 | auto next = runtime_.next();
23 | if (next < Clock::time_point::max()) {
24 | timer_.expires_at(Clock::at(next));
25 | timer_.async_wait(std::bind(&TimeTrigger::on_timeout, this, std::placeholders::_1));
26 | } else {
27 | timer_.cancel();
28 | }
29 | }
30 |
31 | void TimeTrigger::on_timeout(const boost::system::error_code & ec)
32 | {
33 | if (asio::error::operation_aborted != ec) {
34 | schedule();
35 | }
36 | }
37 |
38 | void TimeTrigger::update_runtime()
39 | {
40 | auto current_time = now();
41 | runtime_.trigger(Clock::at(current_time));
42 | }
43 |
--------------------------------------------------------------------------------
/include/autoware_v2x/v2x_app.hpp:
--------------------------------------------------------------------------------
1 | #ifndef V2X_APP_HPP_EUIC2VFR
2 | #define V2X_APP_HPP_EUIC2VFR
3 |
4 | #include "rclcpp/rclcpp.hpp"
5 | #include "std_msgs/msg/string.hpp"
6 | #include "autoware_auto_perception_msgs/msg/predicted_objects.hpp"
7 | #include "tf2_msgs/msg/tf_message.hpp"
8 | #include
9 | #include "autoware_v2x/cpm_application.hpp"
10 | #include "autoware_v2x/time_trigger.hpp"
11 | #include "autoware_v2x/link_layer.hpp"
12 | #include "autoware_v2x/ethernet_device.hpp"
13 | #include "autoware_v2x/positioning.hpp"
14 | #include "autoware_v2x/security.hpp"
15 | #include "autoware_v2x/router_context.hpp"
16 | // #include "autoware_v2x/v2x_node.hpp"
17 |
18 | namespace v2x
19 | {
20 | class V2XNode;
21 | class V2XApp
22 | {
23 | public:
24 | V2XApp(V2XNode *);
25 | void start();
26 | void objectsCallback(const autoware_auto_perception_msgs::msg::PredictedObjects::ConstSharedPtr);
27 | void tfCallback(const tf2_msgs::msg::TFMessage::ConstSharedPtr);
28 |
29 | CpmApplication *cp;
30 | // V2XNode *v2x_node;
31 |
32 | private:
33 | friend class CpmApplication;
34 | friend class Application;
35 | V2XNode* node_;
36 | bool tf_received_;
37 | int tf_interval_;
38 | bool cp_started_;
39 | };
40 | }
41 |
42 | #endif
--------------------------------------------------------------------------------
/src/dcc_passthrough.cpp:
--------------------------------------------------------------------------------
1 | #include "autoware_v2x/dcc_passthrough.hpp"
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include "autoware_v2x/time_trigger.hpp"
9 |
10 | using namespace vanetza;
11 |
12 | DccPassthrough::DccPassthrough(access::Interface & access, TimeTrigger & trigger)
13 | : access_(access), trigger_(trigger)
14 | {
15 | }
16 |
17 | void DccPassthrough::request(const dcc::DataRequest & request, std::unique_ptr packet)
18 | {
19 | if (!allow_packet_flow_) {
20 | std::cout << "ignored request because packet flow is suppressed\n";
21 | return;
22 | }
23 |
24 | trigger_.schedule();
25 |
26 | access::DataRequest acc_req;
27 | acc_req.ether_type = request.ether_type;
28 | acc_req.source_addr = request.source;
29 | acc_req.destination_addr = request.destination;
30 | acc_req.access_category = dcc::map_profile_onto_ac(request.dcc_profile);
31 | access_.request(acc_req, std::move(packet));
32 | }
33 |
34 | void DccPassthrough::allow_packet_flow(bool allow) { allow_packet_flow_ = allow; }
35 |
36 | bool DccPassthrough::allow_packet_flow() { return allow_packet_flow_; }
37 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # AutowareV2X
2 | [](https://youtu.be/57fx3-gUNxU)
3 |
4 | AutowareV2X is an open-source module that can be added onto the newest Autoware.universe to enable V2X communication. If you find this code useful in your research, please consider citing :
5 |
6 | @inproceedings{Asabe2023b,
7 | title = {AutowareV2X: Reliable V2X Communication and Collective Perception for Autonomous Driving},
8 | author = {Yu Asabe and Ehsan Javanmardi and Jin Nakazato and Manabu Tsukada and Hiroshi Esaki},
9 | year = {2023},
10 | date = {2023-06-20},
11 | booktitle = {The 2023 IEEE 97th Vehicular Technology Conference (VTC2023-Spring)},
12 | address = {Florence, Italy},
13 | }
14 |
15 | It utilizes Vanetza as the protocol suite for ETSI C-ITS standards.
16 |
17 | We have also provided a working example of a CPM application where Collective Perception Messages can be used to exchange perception information in Autoware.
18 |
19 |
20 |
21 |
22 |
23 | 
24 |
25 | See the [official documentation](https://tlab-wide.github.io/AutowareV2X/)
26 |
--------------------------------------------------------------------------------
/include/autoware_v2x/raw_socket_link.hpp:
--------------------------------------------------------------------------------
1 | #ifndef RAW_SOCKET_LINK_HPP_VUXH507U
2 | #define RAW_SOCKET_LINK_HPP_VUXH507U
3 |
4 | #include "autoware_v2x/link_layer.hpp"
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 |
12 | class RawSocketLink : public LinkLayer
13 | {
14 | public:
15 | RawSocketLink(boost::asio::generic::raw_protocol::socket&&);
16 | void request(const vanetza::access::DataRequest&, std::unique_ptr) override;
17 | void indicate(IndicationCallback) override;
18 |
19 | protected:
20 | std::size_t transmit(std::unique_ptr);
21 | virtual boost::optional parse_ethernet_header(vanetza::CohesivePacket&) const;
22 |
23 | private:
24 | void do_receive();
25 | void on_read(const boost::system::error_code&, std::size_t);
26 | void pass_up(vanetza::CohesivePacket&&);
27 |
28 | static constexpr std::size_t layers_ = num_osi_layers(vanetza::OsiLayer::Physical, vanetza::OsiLayer::Application);
29 |
30 | boost::asio::generic::raw_protocol::socket socket_;
31 | std::array buffers_;
32 | IndicationCallback callback_;
33 | vanetza::ByteBuffer receive_buffer_;
34 | boost::asio::generic::raw_protocol::endpoint receive_endpoint_;
35 | };
36 |
37 | #endif /* RAW_SOCKET_LINK_HPP_VUXH507U */
38 |
39 |
--------------------------------------------------------------------------------
/include/autoware_v2x/application.hpp:
--------------------------------------------------------------------------------
1 | #ifndef APPLICATION_HPP_PSIGPUTG
2 | #define APPLICATION_HPP_PSIGPUTG
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include "rclcpp/rclcpp.hpp"
11 |
12 | class Application : public vanetza::btp::IndicationInterface
13 | {
14 | public:
15 | using DataConfirm = vanetza::geonet::DataConfirm;
16 | using DataIndication = vanetza::btp::DataIndication;
17 | using DataRequest = vanetza::btp::DataRequestGeoNetParams;
18 | using DownPacketPtr = vanetza::geonet::Router::DownPacketPtr;
19 | using PortType = vanetza::btp::port_type;
20 | using PromiscuousHook = vanetza::btp::PortDispatcher::PromiscuousHook;
21 | using UpPacketPtr = vanetza::geonet::Router::UpPacketPtr;
22 |
23 | Application() = default;
24 | Application(const Application &) = delete;
25 | Application &operator=(const Application &) = delete;
26 | virtual ~Application() = default;
27 |
28 | virtual PortType port() = 0;
29 | virtual PromiscuousHook *promiscuous_hook();
30 |
31 | protected:
32 | DataConfirm request(const DataRequest &, DownPacketPtr, rclcpp::Node*);
33 |
34 | private:
35 | friend class RouterContext;
36 | vanetza::geonet::GbcDataRequest request_gbc(const DataRequest &);
37 | vanetza::geonet::ShbDataRequest request_shb(const DataRequest &);
38 | vanetza::geonet::Router *router_;
39 | };
40 |
41 | #endif /* APPLICATION_HPP_PSIGPUTG */
42 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.5)
2 | project(autoware_v2x)
3 | set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
4 |
5 | # Default to C99
6 | if(NOT CMAKE_C_STANDARD)
7 | set(CMAKE_C_STANDARD 99)
8 | endif()
9 |
10 | # Default to C++14
11 | if(NOT CMAKE_CXX_STANDARD)
12 | set(CMAKE_CXX_STANDARD 14)
13 | endif()
14 |
15 | if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
16 | add_compile_options(-Wall -Wextra -Wpedantic)
17 | endif()
18 |
19 | # find dependencies
20 | find_package(ament_cmake_auto REQUIRED)
21 | set(VANETZA_INSTALL ON)
22 | option(BUILD_SHARED_LIBS "Build shared libraries" ON)
23 | find_package(Vanetza REQUIRED)
24 | find_package(GeographicLib 1.37 REQUIRED)
25 | find_package(Boost COMPONENTS thread REQUIRED)
26 | ament_auto_find_build_dependencies()
27 | find_package(std_msgs REQUIRED)
28 |
29 | include_directories(
30 | include
31 | )
32 |
33 | ament_auto_add_library(autoware_v2x SHARED
34 | src/v2x_node.cpp
35 | src/v2x_app.cpp
36 | src/application.cpp
37 | src/cpm_application.cpp
38 | src/ethernet_device.cpp
39 | src/link_layer.cpp
40 | src/raw_socket_link.cpp
41 | src/router_context.cpp
42 | src/dcc_passthrough.cpp
43 | src/time_trigger.cpp
44 | src/positioning.cpp
45 | src/security.cpp
46 | )
47 |
48 | target_link_libraries(autoware_v2x Vanetza::vanetza ${GeographicLib_LIBRARIES} Boost::thread sqlite3)
49 |
50 | rclcpp_components_register_node(autoware_v2x
51 | PLUGIN "v2x::V2XNode"
52 | EXECUTABLE autoware_v2x_node
53 | )
54 |
55 | if(BUILD_TESTING)
56 | find_package(ament_lint_auto REQUIRED)
57 | ament_lint_auto_find_test_dependencies()
58 | endif()
59 |
60 | ament_auto_package(INSTALL_TO_SHARE
61 | launch
62 | config
63 | )
--------------------------------------------------------------------------------
/include/autoware_v2x/v2x_node.hpp:
--------------------------------------------------------------------------------
1 | #ifndef V2X_NODE_HPP_EUIC2VFR
2 | #define V2X_NODE_HPP_EUIC2VFR
3 |
4 | #include "rclcpp/rclcpp.hpp"
5 | #include "std_msgs/msg/string.hpp"
6 | #include "autoware_auto_perception_msgs/msg/predicted_objects.hpp"
7 | #include "tf2_msgs/msg/tf_message.hpp"
8 | #include
9 | #include "autoware_v2x/v2x_app.hpp"
10 | #include "autoware_v2x/cpm_application.hpp"
11 | #include "autoware_v2x/time_trigger.hpp"
12 | #include "autoware_v2x/link_layer.hpp"
13 | #include "autoware_v2x/ethernet_device.hpp"
14 | #include "autoware_v2x/positioning.hpp"
15 | #include "autoware_v2x/security.hpp"
16 | #include "autoware_v2x/router_context.hpp"
17 | #include
18 |
19 | namespace v2x
20 | {
21 | class V2XNode : public rclcpp::Node
22 | {
23 | public:
24 | explicit V2XNode(const rclcpp::NodeOptions &node_options);
25 | V2XApp *app;
26 | void publishObjects(std::vector *, int cpm_num);
27 | void publishCpmSenderObject(double, double, double);
28 |
29 | std::ofstream latency_log_file;
30 |
31 | private:
32 | void objectsCallback(const autoware_auto_perception_msgs::msg::PredictedObjects::ConstSharedPtr msg);
33 | void tfCallback(const tf2_msgs::msg::TFMessage::ConstSharedPtr msg);
34 |
35 | rclcpp::Subscription::SharedPtr objects_sub_;
36 | rclcpp::Subscription::SharedPtr tf_sub_;
37 | rclcpp::Publisher::SharedPtr cpm_objects_pub_;
38 | rclcpp::Publisher::SharedPtr cpm_sender_pub_;
39 |
40 | double pos_lat_;
41 | double pos_lon_;
42 | };
43 | }
44 |
45 | #endif
--------------------------------------------------------------------------------
/src/ethernet_device.cpp:
--------------------------------------------------------------------------------
1 | #include "autoware_v2x/ethernet_device.hpp"
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 |
10 | static void initialize(ifreq& request, const char* interface_name)
11 | {
12 | std::memset(&request, 0, sizeof(ifreq));
13 | std::strncpy(request.ifr_name, interface_name, IF_NAMESIZE);
14 | request.ifr_name[IF_NAMESIZE - 1] = '\0';
15 | }
16 |
17 | EthernetDevice::EthernetDevice(const char* devname) :
18 | local_socket_(::socket(AF_LOCAL, SOCK_DGRAM, 0)),
19 | interface_name_(devname)
20 | {
21 | if (!local_socket_) {
22 | throw std::system_error(errno, std::system_category());
23 | }
24 | }
25 |
26 | EthernetDevice::~EthernetDevice()
27 | {
28 | if (local_socket_ >= 0)
29 | ::close(local_socket_);
30 | }
31 |
32 | EthernetDevice::protocol::endpoint EthernetDevice::endpoint(int family) const
33 | {
34 | sockaddr_ll socket_address = {0};
35 | socket_address.sll_family = family;
36 | socket_address.sll_protocol = htons(ETH_P_ALL);
37 | socket_address.sll_ifindex = index();
38 | return protocol::endpoint(&socket_address, sizeof(sockaddr_ll));
39 | }
40 |
41 | int EthernetDevice::index() const
42 | {
43 | ifreq data;
44 | initialize(data, interface_name_.c_str());
45 | ::ioctl(local_socket_, SIOCGIFINDEX, &data);
46 | return data.ifr_ifindex;
47 | }
48 |
49 | vanetza::MacAddress EthernetDevice::address() const
50 | {
51 | ifreq data;
52 | initialize(data, interface_name_.c_str());
53 | ::ioctl(local_socket_, SIOCGIFHWADDR, &data);
54 |
55 | vanetza::MacAddress addr;
56 | std::copy_n(data.ifr_hwaddr.sa_data, addr.octets.size(), addr.octets.data());
57 | return addr;
58 | }
59 |
--------------------------------------------------------------------------------
/include/autoware_v2x/router_context.hpp:
--------------------------------------------------------------------------------
1 | #ifndef ROUTER_CONTEXT_HPP_KIPUYBY2
2 | #define ROUTER_CONTEXT_HPP_KIPUYBY2
3 |
4 | #include "autoware_v2x/dcc_passthrough.hpp"
5 | #include "autoware_v2x/link_layer.hpp"
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 |
14 | class Application;
15 | class TimeTrigger;
16 |
17 | class RouterContext
18 | {
19 | public:
20 | RouterContext(const vanetza::geonet::MIB&, TimeTrigger&, vanetza::PositionProvider&, vanetza::security::SecurityEntity*);
21 | ~RouterContext();
22 | void enable(Application*);
23 | void disable(Application*);
24 |
25 | /**
26 | * Allow/disallow transmissions without GNSS position fix
27 | *
28 | * \param flag true if transmissions shall be dropped when no GNSS position fix is available
29 | */
30 | void require_position_fix(bool flag);
31 |
32 | void set_link_layer(LinkLayer*);
33 | void updateMIB(const vanetza::geonet::MIB&);
34 |
35 | private:
36 | void indicate(vanetza::CohesivePacket&& packet, const vanetza::EthernetHeader& hdr);
37 | void log_packet_drop(vanetza::geonet::Router::PacketDropReason);
38 | void update_position_vector();
39 | void update_packet_flow(const vanetza::geonet::LongPositionVector&);
40 |
41 | vanetza::geonet::MIB mib_;
42 | vanetza::geonet::Router router_;
43 | TimeTrigger& trigger_;
44 | vanetza::PositionProvider& positioning_;
45 | vanetza::btp::PortDispatcher dispatcher_;
46 | std::unique_ptr request_interface_;
47 | std::list applications_;
48 | bool require_position_fix_ = false;
49 | };
50 |
51 | #endif /* ROUTER_CONTEXT_HPP_KIPUYBY2 */
52 |
--------------------------------------------------------------------------------
/mkdocs.yaml:
--------------------------------------------------------------------------------
1 | site_name: AutowareV2X Documentation
2 | site_url: https://tlab-wide.github.io/AutowareV2X
3 | repo_url: https://github.com/tlab-wide/AutowareV2X
4 | docs_dir: docs
5 | copyright: Copyright © 2023 Yu Asabe
6 |
7 | theme:
8 | name: material
9 | features:
10 | - navigation.expand
11 | - navigation.indexes
12 | - navigation.instant
13 | - navigation.sections
14 | - navigation.tabs
15 | - navigation.tabs.sticky
16 | - navigation.top
17 | palette:
18 | - scheme: default
19 | primary: white
20 | toggle:
21 | icon: material/weather-sunny
22 | name: Switch to dark mode
23 | - scheme: slate
24 | primary: grey
25 | toggle:
26 | icon: material/weather-night
27 | name: Switch to light mode
28 | language: en
29 |
30 | extra:
31 | font:
32 | text: Roboto
33 | code: Roboto Mono
34 | version:
35 | provider: mike
36 |
37 | extra_css:
38 | - https://use.fontawesome.com/releases/v5.15.4/css/all.css
39 | - stylesheets/extra.css
40 |
41 | extra_javascript:
42 | - https://cdn.jsdelivr.net/npm/mathjax@2/MathJax.js?config=TeX-AMS-MML_HTMLorMML
43 |
44 | plugins:
45 | - awesome-pages
46 | - search
47 |
48 | markdown_extensions:
49 | - abbr
50 | - admonition
51 | - attr_list
52 | - codehilite:
53 | guess_lang: false
54 | - fontawesome_markdown
55 | - footnotes
56 | - mdx_math
57 | - plantuml_markdown:
58 | server: http://www.plantuml.com/plantuml
59 | format: svg
60 | - pymdownx.arithmatex
61 | - pymdownx.details
62 | - pymdownx.highlight
63 | - pymdownx.snippets:
64 | auto_append:
65 | - includes/abbreviations.md
66 | - pymdownx.superfences:
67 | custom_fences:
68 | - name: mermaid
69 | class: mermaid
70 | format: !!python/name:pymdownx.superfences.fence_code_format
71 | - toc:
72 | permalink: "#"
73 | toc_depth: 3
--------------------------------------------------------------------------------
/docs/design/index.md:
--------------------------------------------------------------------------------
1 | # Design
2 |
3 | !!! Warning
4 | More to come
5 |
6 | A V2X communication software stack called [Vanetza](https://github.com/riebl/vanetza) is integrated into the standalone autonomous driving software stack, [Autoware](https://github.com/autowarefoundation/autoware). The V2X stack and the autonomous driving stack can be decoupled, allowing other applications to utilize the V2X router as well. A high-level overview of the architecture is shown below.
7 |
8 | 
9 |
10 | Autoware is responsible for the perception task, while AutowareV2X manages the transmission and reception of messages over the V2X channel. Services that are necessary for the integration of Vanetza into Autoware were newly developed.
11 |
12 | The V2XApp is responsible for managing the various facilities such as DENM, CAM, CPM, while the V2XNode handles the conversion of information between the V2X messages and ROS2 messages.
13 |
14 | ## V2XNode
15 |
16 | The V2XNode launches a ROS2 Node for AutowareV2X. Its main purpose is to act as the bridge interface between Autoware and AutowareV2X. Information that is to be utilized in V2X Applications are retreived from Autoware in the form of ROS2 topics. Similarily, information that is received by AutowareV2X through V2X communications is published as ROS2 topics in order to feed it back into Autoware.
17 |
18 | ### Input
19 |
20 | | Name | Type | Description |
21 | | -------------------- | ------------------------------- | ---------------- |
22 | | `/perception/object_recognition/objects` | `autoware_auto_perception_msgs::msg::PredictedObjects` | Perceived Objects by Autoware |
23 | | `/tf` | `tf2_msgs::msg::TFMessage` | Pose of Ego Vehicle |
24 |
25 | ### Output
26 |
27 | | Name | Type | Description |
28 | | -------------------- | ------------------------------- | ---------------- |
29 | | `/v2x/cpm/objects` | `autoware_auto_perception_msgs::msg::PredictedObjects` | Objects received by CPMs |
30 |
31 | ### Functions
32 |
33 | | Name | Description |
34 | | -------------------- | ---------------- |
35 | | `objectsCallback(const autoware_auto_perception_msgs::msg::PredictedObjects::ConstSharedPtr msg)` | Call `V2XApp::objectsCallback` |
36 | | `tfCallback` | Call `V2XApp::tfCallback` |
37 | | `publishObjects(std::vector *objectsStack, int cpm_num)` | |
38 | | `publishCpmSenderObject` | Not used now |
39 |
40 |
41 | ## V2XApp
42 |
43 | ## CPM Facility
--------------------------------------------------------------------------------
/src/application.cpp:
--------------------------------------------------------------------------------
1 | #include "autoware_v2x/application.hpp"
2 | #include "rclcpp/rclcpp.hpp"
3 | #include
4 | #include
5 | #include
6 |
7 | using namespace vanetza;
8 |
9 | Application::DataConfirm Application::request(const DataRequest &request, DownPacketPtr packet, rclcpp::Node* node)
10 | {
11 | // RCLCPP_INFO(node->get_logger(), "Inside Application::request 0");
12 | DataConfirm confirm(DataConfirm::ResultCode::Rejected_Unspecified);
13 | // RCLCPP_INFO(node->get_logger(), "Inside Application::request 1");
14 | if (packet) {
15 | // RCLCPP_INFO(node->get_logger(), "Inside Application::request 2");
16 | }
17 | if (router_ && packet)
18 | {
19 | // RCLCPP_INFO(node->get_logger(), "Inside Application::request 3");
20 | btp::HeaderB btp_header;
21 | btp_header.destination_port = this->port();
22 | btp_header.destination_port_info = host_cast(0);
23 | packet->layer(OsiLayer::Transport) = btp_header;
24 |
25 | switch (request.transport_type)
26 | {
27 | case geonet::TransportType::SHB:
28 | // RCLCPP_INFO(node->get_logger(), "Inside Application::request 4");
29 | confirm = router_->request(request_shb(request), std::move(packet));
30 | break;
31 | case geonet::TransportType::GBC:
32 | confirm = router_->request(request_gbc(request), std::move(packet));
33 | break;
34 | default:
35 | // TODO remaining transport types are not implemented
36 | break;
37 | }
38 | }
39 |
40 | return confirm;
41 | }
42 |
43 | void initialize_request(const Application::DataRequest &generic, geonet::DataRequest &geonet)
44 | {
45 | geonet.upper_protocol = geonet::UpperProtocol::BTP_B;
46 | geonet.communication_profile = generic.communication_profile;
47 | geonet.its_aid = generic.its_aid;
48 | if (generic.maximum_lifetime)
49 | {
50 | geonet.maximum_lifetime = generic.maximum_lifetime.get();
51 | }
52 | geonet.repetition = generic.repetition;
53 | geonet.traffic_class = generic.traffic_class;
54 | }
55 |
56 | geonet::GbcDataRequest Application::request_gbc(const DataRequest &generic)
57 | {
58 | assert(router_);
59 | geonet::GbcDataRequest gbc(router_->get_mib());
60 | initialize_request(generic, gbc);
61 | gbc.destination = boost::get(generic.destination);
62 | return gbc;
63 | }
64 |
65 | geonet::ShbDataRequest Application::request_shb(const DataRequest &generic)
66 | {
67 | assert(router_);
68 | geonet::ShbDataRequest shb(router_->get_mib());
69 | initialize_request(generic, shb);
70 | return shb;
71 | }
72 |
73 | Application::PromiscuousHook *Application::promiscuous_hook()
74 | {
75 | return nullptr;
76 | }
77 |
--------------------------------------------------------------------------------
/src/raw_socket_link.cpp:
--------------------------------------------------------------------------------
1 | #include "autoware_v2x/raw_socket_link.hpp"
2 | #include
3 | #include
4 | #include
5 | #include
6 |
7 | using namespace vanetza;
8 |
9 | RawSocketLink::RawSocketLink(boost::asio::generic::raw_protocol::socket && socket)
10 | : socket_(std::move(socket)),
11 | receive_buffer_(2048, 0x00),
12 | receive_endpoint_(socket_.local_endpoint())
13 | {
14 | do_receive();
15 | }
16 |
17 | void RawSocketLink::request(
18 | const access::DataRequest & request, std::unique_ptr packet)
19 | {
20 | packet->layer(OsiLayer::Link) =
21 | create_ethernet_header(request.destination_addr, request.source_addr, request.ether_type);
22 | transmit(std::move(packet));
23 | }
24 |
25 | std::size_t RawSocketLink::transmit(std::unique_ptr packet)
26 | {
27 | std::array const_buffers;
28 | for (auto & layer : osi_layer_range()) {
29 | const auto index = distance(OsiLayer::Physical, layer);
30 | packet->layer(layer).convert(buffers_[index]);
31 | const_buffers[index] = boost::asio::buffer(buffers_[index]);
32 | }
33 |
34 | return socket_.send(const_buffers);
35 | }
36 |
37 | void RawSocketLink::indicate(IndicationCallback callback) {
38 | callback_ = callback;
39 | }
40 |
41 | void RawSocketLink::do_receive()
42 | {
43 | namespace sph = std::placeholders;
44 | socket_.async_receive_from(
45 | boost::asio::buffer(receive_buffer_), receive_endpoint_,
46 | std::bind(&RawSocketLink::on_read, this, sph::_1, sph::_2));
47 | }
48 |
49 | void RawSocketLink::on_read(const boost::system::error_code & ec, std::size_t read_bytes)
50 | {
51 | if (!ec) {
52 | ByteBuffer buffer(receive_buffer_.begin(), receive_buffer_.begin() + read_bytes);
53 | CohesivePacket packet(std::move(buffer), OsiLayer::Physical);
54 | boost::optional eth = parse_ethernet_header(packet);
55 | if (callback_ && eth) {
56 | callback_(std::move(packet), *eth);
57 | }
58 | do_receive();
59 | }
60 | }
61 |
62 | boost::optional RawSocketLink::parse_ethernet_header(
63 | vanetza::CohesivePacket & packet) const
64 | {
65 | packet.set_boundary(OsiLayer::Physical, 0);
66 | if (packet.size(OsiLayer::Link) < EthernetHeader::length_bytes) {
67 | std::cerr << "Router dropped invalid packet (too short for Ethernet header)\n";
68 | } else {
69 | packet.set_boundary(OsiLayer::Link, EthernetHeader::length_bytes);
70 | auto link_range = packet[OsiLayer::Link];
71 | return decode_ethernet_header(link_range.begin(), link_range.end());
72 | }
73 |
74 | return boost::none;
75 | }
76 |
--------------------------------------------------------------------------------
/cmake/FindGeographicLib.cmake:
--------------------------------------------------------------------------------
1 | option(GeographicLib_PREFER_PACKAGE_CONFIG "Prefer CMake config mode" ON)
2 | mark_as_advanced(GeographicLib_PREFER_PACKAGE_CONFIG)
3 |
4 | if(GeographicLib_PREFER_PACKAGE_CONFIG)
5 | find_package(GeographicLib ${GeographicLib_FIND_VERSION} QUIET CONFIG)
6 | if (GeographicLib_FOUND)
7 | message(STATUS "GeographicLib: using found CMake configuration")
8 | return()
9 | endif()
10 | endif()
11 | message(STATUS "GeographicLib: using lookup code by Vanetza")
12 |
13 | find_path(GeographicLib_INCLUDE_DIR NAMES GeographicLib/Config.h DOC "GeographicLib include directory")
14 | find_library(GeographicLib_LIBRARY_RELEASE NAMES Geographic DOC "GeographicLib library (release)")
15 | find_library(GeographicLib_LIBRARY_DEBUG NAMES Geographic_d DOC "GeographicLib library (debug)")
16 |
17 | include(SelectLibraryConfigurations)
18 | select_library_configurations(GeographicLib)
19 |
20 | if(GeographicLib_INCLUDE_DIR)
21 | file(STRINGS ${GeographicLib_INCLUDE_DIR}/GeographicLib/Config.h _config_version REGEX "GEOGRAPHICLIB_VERSION_STRING")
22 | string(REGEX MATCH "\"([0-9.]+)\"$" _match_version ${_config_version})
23 | set(GeographicLib_VERSION_STRING ${CMAKE_MATCH_1})
24 | endif()
25 |
26 | include(FindPackageHandleStandardArgs)
27 | find_package_handle_standard_args(GeographicLib
28 | REQUIRED_VARS GeographicLib_INCLUDE_DIR GeographicLib_LIBRARY
29 | FOUND_VAR GeographicLib_FOUND
30 | VERSION_VAR GeographicLib_VERSION_STRING)
31 |
32 | if(GeographicLib_FOUND AND NOT TARGET GeographicLib::GeographicLib)
33 | add_library(GeographicLib::GeographicLib UNKNOWN IMPORTED)
34 | set_target_properties(GeographicLib::GeographicLib PROPERTIES
35 | INTERFACE_INCLUDE_DIRECTORIES "${GeographicLib_INCLUDE_DIR}"
36 | IMPORTED_LOCATION "${GeographicLib_LIBRARY}"
37 | IMPORTED_LINK_INTERFACE_LANGUAGES "CXX")
38 | if(EXISTS "${GeographicLib_LIBRARY_RELEASE}")
39 | set_property(TARGET GeographicLib::GeographicLib APPEND PROPERTY
40 | IMPORTED_CONFIGURATIONS RELEASE)
41 | set_target_properties(GeographicLib::GeographicLib PROPERTIES
42 | IMPORTED_LOCATION_RELEASE "${GeographicLib_LIBRARY_RELEASE}")
43 | endif()
44 | if(EXISTS "${GeographicLib_LIBRARY_DEBUG}")
45 | set_property(TARGET GeographicLib::GeographicLib APPEND PROPERTY
46 | IMPORTED_CONFIGURATIONS DEBUG)
47 | set_target_properties(GeographicLib::GeographicLib PROPERTIES
48 | IMPORTED_LOCATION_DEBUG "${GeographicLib_LIBRARY_DEBUG}")
49 | endif()
50 | endif()
51 |
52 | mark_as_advanced(GeographicLib_INCLUDE_DIR GeographicLib_LIBRARY)
53 | set(GeographicLib_INCLUDE_DIRS ${GeographicLib_INCLUDE_DIR})
54 | # GeographicLib_LIBRARIES is set by SelectLibraryConfigurations
55 |
--------------------------------------------------------------------------------
/src/security.cpp:
--------------------------------------------------------------------------------
1 | #include "autoware_v2x/security.hpp"
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 |
13 | using namespace vanetza;
14 | namespace po = boost::program_options;
15 |
16 | class SecurityContext : public security::SecurityEntity
17 | {
18 | public:
19 | SecurityContext(const Runtime &runtime, PositionProvider &positioning) : runtime(runtime), positioning(positioning),
20 | backend(security::create_backend("default")),
21 | sign_header_policy(runtime, positioning),
22 | cert_cache(runtime),
23 | cert_validator(*backend, cert_cache, trust_store)
24 | {
25 | }
26 |
27 | security::EncapConfirm encapsulate_packet(security::EncapRequest &&request) override
28 | {
29 | if (!entity)
30 | {
31 | throw std::runtime_error("security entity is not ready");
32 | }
33 | return entity->encapsulate_packet(std::move(request));
34 | }
35 |
36 | security::DecapConfirm decapsulate_packet(security::DecapRequest &&request) override
37 | {
38 | if (!entity)
39 | {
40 | throw std::runtime_error("security entity is not ready");
41 | }
42 | return entity->decapsulate_packet(std::move(request));
43 | }
44 |
45 | void build_entity()
46 | {
47 | if (!cert_provider)
48 | {
49 | throw std::runtime_error("certificate provider is missing");
50 | }
51 | security::SignService sign_service = straight_sign_service(*cert_provider, *backend, sign_header_policy);
52 | security::VerifyService verify_service = straight_verify_service(runtime, *cert_provider, cert_validator,
53 | *backend, cert_cache, sign_header_policy, positioning);
54 | entity.reset(new security::DelegatingSecurityEntity{sign_service, verify_service});
55 | }
56 |
57 | const Runtime &runtime;
58 | PositionProvider &positioning;
59 | std::unique_ptr backend;
60 | std::unique_ptr entity;
61 | std::unique_ptr cert_provider;
62 | security::DefaultSignHeaderPolicy sign_header_policy;
63 | security::TrustStore trust_store;
64 | security::CertificateCache cert_cache;
65 | security::DefaultCertificateValidator cert_validator;
66 | };
67 |
68 | std::unique_ptr
69 | create_security_entity(const Runtime &runtime, PositionProvider &positioning)
70 | {
71 | std::unique_ptr security;
72 |
73 | return security;
74 | }
75 |
--------------------------------------------------------------------------------
/include/autoware_v2x/cpm_application.hpp:
--------------------------------------------------------------------------------
1 | #ifndef CPM_APPLICATION_HPP_EUIC2VFR
2 | #define CPM_APPLICATION_HPP_EUIC2VFR
3 |
4 | #include "autoware_v2x/application.hpp"
5 | #include "rclcpp/rclcpp.hpp"
6 | #include
7 | #include
8 | #include "autoware_auto_perception_msgs/msg/predicted_objects.hpp"
9 | #include "autoware_v2x/positioning.hpp"
10 | #include
11 |
12 | namespace v2x
13 | {
14 | class V2XNode;
15 | class CpmApplication : public Application
16 | {
17 | public:
18 | CpmApplication(V2XNode *node, vanetza::Runtime &, bool is_sender);
19 | PortType port() override;
20 | std::string uuidToHexString(const unique_identifier_msgs::msg::UUID&);
21 | void indicate(const DataIndication &, UpPacketPtr) override;
22 | void set_interval(vanetza::Clock::duration);
23 | void setAllObjectsOfPersonsAnimalsToSend(const autoware_auto_perception_msgs::msg::PredictedObjects::ConstSharedPtr);
24 | void updateObjectsList(const autoware_auto_perception_msgs::msg::PredictedObjects::ConstSharedPtr);
25 | void updateMGRS(double *, double *);
26 | void updateRP(double *, double *, double *);
27 | void updateGenerationTime(int *, long *);
28 | void updateHeading(double *);
29 | void printObjectsList(int);
30 | void send();
31 |
32 | /** Creates the database schema.
33 | * Creates tables for cpm_sent, cpm_received
34 | */
35 | void createTables();
36 |
37 | /** Inserts CPM into the database.
38 | * Constructs and executes queries for inserting the specified CPM data.
39 | * @param cpm The CPM to be inserted
40 | * @param table_name The table to insert the CPM into (cpm_sent or cpm_received)
41 | */
42 | void insertCpmToCpmTable(vanetza::asn1::Cpm, char*);
43 |
44 | struct Object {
45 | std::string uuid;
46 | int objectID; // 0-255 for CPM
47 | vanetza::Clock::time_point timestamp;
48 | rclcpp::Time timestamp_ros;
49 | double position_x;
50 | double position_y;
51 | double position_z;
52 | double orientation_x;
53 | double orientation_y;
54 | double orientation_z;
55 | double orientation_w;
56 | double twist_linear_x;
57 | double twist_linear_y;
58 | double twist_angular_x;
59 | double twist_angular_y;
60 | int shape_x;
61 | int shape_y;
62 | int shape_z;
63 | int xDistance;
64 | int yDistance;
65 | int xSpeed;
66 | int ySpeed;
67 | int yawAngle;
68 | vanetza::PositionFix position;
69 | int timeOfMeasurement;
70 | bool to_send;
71 | int to_send_trigger;
72 | };
73 | std::vector objectsList;
74 | std::vector receivedObjectsStack;
75 |
76 | private:
77 | void schedule_timer();
78 | void on_timer(vanetza::Clock::time_point);
79 |
80 | V2XNode *node_;
81 | vanetza::Runtime &runtime_;
82 | vanetza::Clock::duration cpm_interval_;
83 |
84 | struct Ego_station {
85 | double mgrs_x;
86 | double mgrs_y;
87 | double latitude;
88 | double longitude;
89 | double altitude;
90 | double heading;
91 | };
92 |
93 | Ego_station ego_;
94 |
95 | int generationTime_;
96 | long gdt_timestamp_;
97 |
98 | double objectConfidenceThreshold_;
99 |
100 | bool updating_objects_list_;
101 | bool sending_;
102 | bool is_sender_;
103 | bool reflect_packet_;
104 | bool include_all_persons_and_animals_;
105 |
106 | int cpm_num_;
107 | int received_cpm_num_;
108 |
109 | int cpm_object_id_;
110 |
111 | bool use_dynamic_generation_rules_;
112 | };
113 | }
114 |
115 | #endif /* CPM_APPLICATION_HPP_EUIC2VFR */
116 |
--------------------------------------------------------------------------------
/src/router_context.cpp:
--------------------------------------------------------------------------------
1 | #include "autoware_v2x/application.hpp"
2 | #include "autoware_v2x/dcc_passthrough.hpp"
3 | #include "autoware_v2x/ethernet_device.hpp"
4 | #include "autoware_v2x/router_context.hpp"
5 | #include "autoware_v2x/time_trigger.hpp"
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 |
12 | using namespace vanetza;
13 |
14 | RouterContext::RouterContext(const geonet::MIB &mib, TimeTrigger &trigger, vanetza::PositionProvider &positioning, vanetza::security::SecurityEntity *security_entity) : mib_(mib), router_(trigger.runtime(), mib_), trigger_(trigger), positioning_(positioning)
15 | {
16 | router_.packet_dropped = std::bind(&RouterContext::log_packet_drop, this, std::placeholders::_1);
17 | router_.set_address(mib_.itsGnLocalGnAddr);
18 | router_.set_transport_handler(geonet::UpperProtocol::BTP_B, &dispatcher_);
19 | router_.set_security_entity(security_entity);
20 | mib_.vanetzaDisableBeaconing = true;
21 | update_position_vector();
22 |
23 | trigger_.schedule();
24 | }
25 |
26 | RouterContext::~RouterContext()
27 | {
28 | for (auto *app : applications_)
29 | {
30 | disable(app);
31 | }
32 | }
33 |
34 | void RouterContext::updateMIB(const geonet::MIB &mib) {
35 | mib_ = mib;
36 | router_.set_address(mib_.itsGnLocalGnAddr);
37 | }
38 |
39 | void RouterContext::log_packet_drop(geonet::Router::PacketDropReason reason)
40 | {
41 | auto reason_string = stringify(reason);
42 | std::cout << "Router dropped packet because of " << reason_string << " (" << static_cast(reason) << ")\n";
43 | }
44 |
45 | void RouterContext::set_link_layer(LinkLayer *link_layer)
46 | {
47 | namespace dummy = std::placeholders;
48 |
49 | if (link_layer)
50 | {
51 | request_interface_.reset(new DccPassthrough{*link_layer, trigger_});
52 | router_.set_access_interface(request_interface_.get());
53 | link_layer->indicate(std::bind(&RouterContext::indicate, this, dummy::_1, dummy::_2));
54 | update_packet_flow(router_.get_local_position_vector());
55 | }
56 | else
57 | {
58 | router_.set_access_interface(nullptr);
59 | request_interface_.reset();
60 | }
61 | }
62 |
63 | void RouterContext::indicate(CohesivePacket &&packet, const EthernetHeader &hdr)
64 | {
65 | if (hdr.source != mib_.itsGnLocalGnAddr.mid() && hdr.type == access::ethertype::GeoNetworking)
66 | {
67 | std::cout << "received packet from " << hdr.source << " (" << packet.size() << " bytes)\n";
68 | std::unique_ptr up{new PacketVariant(std::move(packet))};
69 | trigger_.schedule(); // ensure the clock is up-to-date for the security entity
70 | router_.indicate(std::move(up), hdr.source, hdr.destination);
71 | trigger_.schedule(); // schedule packet forwarding
72 | }
73 | }
74 |
75 | void RouterContext::enable(Application *app)
76 | {
77 | app->router_ = &router_;
78 |
79 | dispatcher_.add_promiscuous_hook(app->promiscuous_hook());
80 | if (app->port() != btp::port_type(0))
81 | {
82 | dispatcher_.set_non_interactive_handler(app->port(), app);
83 | }
84 | }
85 |
86 | void RouterContext::disable(Application *app)
87 | {
88 | if (app->port() != btp::port_type(0))
89 | {
90 | dispatcher_.set_non_interactive_handler(app->port(), nullptr);
91 | }
92 | dispatcher_.remove_promiscuous_hook(app->promiscuous_hook());
93 |
94 | app->router_ = nullptr;
95 | }
96 |
97 | void RouterContext::require_position_fix(bool flag)
98 | {
99 | require_position_fix_ = flag;
100 | update_packet_flow(router_.get_local_position_vector());
101 | }
102 |
103 | void RouterContext::update_position_vector()
104 | {
105 | router_.update_position(positioning_.position_fix());
106 | vanetza::Runtime::Callback callback = [this](vanetza::Clock::time_point)
107 | { this->update_position_vector(); };
108 | vanetza::Clock::duration next = std::chrono::seconds(1);
109 | trigger_.runtime().schedule(next, callback);
110 | trigger_.schedule();
111 |
112 | update_packet_flow(router_.get_local_position_vector());
113 | }
114 |
115 | void RouterContext::update_packet_flow(const geonet::LongPositionVector &lpv)
116 | {
117 | if (request_interface_)
118 | {
119 | if (require_position_fix_)
120 | {
121 | // Skip all requests until a valid GPS position is available
122 | request_interface_->allow_packet_flow(lpv.position_accuracy_indicator);
123 | }
124 | else
125 | {
126 | request_interface_->allow_packet_flow(true);
127 | }
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/src/v2x_app.cpp:
--------------------------------------------------------------------------------
1 | #include "autoware_v2x/v2x_app.hpp"
2 | #include "autoware_v2x/v2x_node.hpp"
3 | #include "autoware_v2x/time_trigger.hpp"
4 | #include "autoware_v2x/router_context.hpp"
5 | #include "autoware_v2x/positioning.hpp"
6 | #include "autoware_v2x/security.hpp"
7 | #include "autoware_v2x/link_layer.hpp"
8 | #include "autoware_v2x/cpm_application.hpp"
9 |
10 | #include "rclcpp/rclcpp.hpp"
11 | #include "std_msgs/msg/string.hpp"
12 |
13 | #include "tf2/LinearMath/Quaternion.h"
14 | #include "tf2/LinearMath/Matrix3x3.h"
15 |
16 | #include
17 | #include
18 | #include
19 | #include
20 | #include
21 | #include
22 | #include
23 |
24 | namespace gn = vanetza::geonet;
25 |
26 | using namespace vanetza;
27 | using namespace std::chrono;
28 |
29 | namespace v2x
30 | {
31 | V2XApp::V2XApp(V2XNode *node) :
32 | node_(node),
33 | tf_received_(false),
34 | tf_interval_(0),
35 | cp_started_(false)
36 | {
37 | }
38 |
39 | void V2XApp::objectsCallback(const autoware_auto_perception_msgs::msg::PredictedObjects::ConstSharedPtr msg) {
40 | // RCLCPP_INFO(node_->get_logger(), "V2XApp: msg received");
41 | if (!tf_received_) {
42 | RCLCPP_WARN(node_->get_logger(), "[V2XApp::objectsCallback] tf not received yet");
43 | }
44 | if (tf_received_ && cp_started_) {
45 | cp->updateObjectsList(msg);
46 | }
47 | }
48 |
49 | void V2XApp::tfCallback(const tf2_msgs::msg::TFMessage::ConstSharedPtr msg) {
50 |
51 | tf_received_ = true;
52 |
53 | double x = msg->transforms[0].transform.translation.x;
54 | double y = msg->transforms[0].transform.translation.y;
55 | double z = msg->transforms[0].transform.translation.z;
56 |
57 | long timestamp_sec = msg->transforms[0].header.stamp.sec; // seconds
58 | long timestamp_nsec = msg->transforms[0].header.stamp.nanosec; // nanoseconds
59 | timestamp_sec -= 1072915200; // convert to etsi-epoch
60 | long timestamp_msec = timestamp_sec * 1000 + timestamp_nsec / 1000000;
61 | int gdt = timestamp_msec % 65536;
62 |
63 | double rot_x = msg->transforms[0].transform.rotation.x;
64 | double rot_y = msg->transforms[0].transform.rotation.y;
65 | double rot_z = msg->transforms[0].transform.rotation.z;
66 | double rot_w = msg->transforms[0].transform.rotation.w;
67 |
68 | // Convert the quarternion to euler (yaw, pitch, roll)
69 | tf2::Quaternion quat(rot_x, rot_y, rot_z, rot_w);
70 | tf2::Matrix3x3 matrix(quat);
71 | double roll, pitch, yaw;
72 | matrix.getRPY(roll, pitch, yaw);
73 |
74 | int zone = 54;
75 | int grid_num_x = 4;
76 | int grid_num_y = 39;
77 | int grid_size = 100000;
78 | bool northp = true;
79 | double lat, lon;
80 |
81 | // Reverse projection from UTM to geographic.
82 | GeographicLib::UTMUPS::Reverse(
83 | zone,
84 | northp,
85 | grid_num_x * grid_size + x,
86 | grid_num_y * grid_size + y,
87 | lat,
88 | lon
89 | );
90 |
91 | if (cp && cp_started_) {
92 | cp->updateMGRS(&x, &y);
93 | cp->updateRP(&lat, &lon, &z);
94 | cp->updateHeading(&yaw);
95 | cp->updateGenerationTime(&gdt, ×tamp_msec);
96 | }
97 | }
98 |
99 | void V2XApp::start() {
100 | RCLCPP_INFO(node_->get_logger(), "V2X App Launched");
101 |
102 | boost::asio::io_service io_service;
103 | TimeTrigger trigger(io_service);
104 |
105 | std::string network_interface;
106 | node_->get_parameter("network_interface", network_interface);
107 | RCLCPP_INFO(node_->get_logger(), "Network Interface: %s", network_interface.c_str());
108 |
109 | EthernetDevice device(network_interface.c_str());
110 | vanetza::MacAddress mac_address = device.address();
111 |
112 | std::stringstream sout;
113 | sout << mac_address;
114 | RCLCPP_INFO(node_->get_logger(), "MAC Address: '%s'", sout.str().c_str());
115 |
116 | // Geonetworking Management Infirmation Base (MIB) defines the GN protocol constants.
117 | gn::MIB mib;
118 | mib.itsGnLocalGnAddr.mid(mac_address);
119 | mib.itsGnLocalGnAddr.is_manually_configured(true);
120 | mib.itsGnLocalAddrConfMethod = geonet::AddrConfMethod::Managed;
121 | mib.itsGnSecurity = false;
122 | mib.itsGnProtocolVersion = 1;
123 |
124 | // Create raw socket on device and LinkLayer object
125 | auto link_layer = create_link_layer(io_service, device, "ethernet");
126 | auto positioning = create_position_provider(io_service, trigger.runtime());
127 | auto security = create_security_entity(trigger.runtime(), *positioning);
128 | RouterContext context(mib, trigger, *positioning, security.get());
129 |
130 | context.set_link_layer(link_layer.get());
131 |
132 | bool is_sender;
133 | node_->get_parameter("is_sender", is_sender);
134 | cp = new CpmApplication(node_, trigger.runtime(), is_sender);
135 |
136 | context.enable(cp);
137 |
138 | cp_started_ = true;
139 |
140 | io_service.run();
141 | }
142 | }
--------------------------------------------------------------------------------
/docs/installation/source-installation.md:
--------------------------------------------------------------------------------
1 | # Source Installation
2 |
3 | ## Prerequisites
4 |
5 | - OS
6 | - Ubuntu 20.04
7 | - Ubuntu 22.04
8 | - ROS
9 | - ROS2 Galactic
10 |
11 | ## Installing Autoware
12 |
13 | Refer to the [Official Autoware Documentation](https://autowarefoundation.github.io/autoware-documentation/main/installation/autoware/source-installation/) for the newest installation procedures. In a nutshell, you can run the following commands:
14 |
15 | ```bash
16 | # Clone repository
17 | git clone https://github.com/autowarefoundation/autoware.git
18 | cd autoware
19 |
20 | # Install dependencies using Ansible
21 | ./setup-dev-env.sh
22 |
23 | # Use vcstool to import more repositories
24 | mkdir src
25 | vcs import src < autoware.repos
26 |
27 | # Install dependent ROS packages
28 | source /opt/ros/galactic/setup.bash
29 | rosdep update
30 | rosdep install -y --from-paths src --ignore-src --rosdistro $ROS_DISTRO
31 |
32 | # Build the workspace
33 | colcon build --symlink-install --cmake-args -DCMAKE_BUILD_TYPE=Release
34 | ```
35 |
36 | ## Adding AutowareV2X
37 |
38 | 1. Edit the `autoware.repos` file and replace the following repositories.
39 | ```
40 | repositories:
41 | core/autoware.core:
42 | type: git
43 | url: https://github.com/autowarefoundation/autoware.core.git
44 | version: 6bafedfb24fb34157ed65bfe3f6f4c1ed0fbc80b
45 | core/autoware_adapi_msgs:
46 | type: git
47 | url: https://github.com/autowarefoundation/autoware_adapi_msgs.git
48 | version: 9679b5a7a1f4cfff2fa50b80d2759d3937f2f953
49 | core/autoware_common:
50 | type: git
51 | url: https://github.com/autowarefoundation/autoware_common.git
52 | version: 6916df26fafe6749db4b1d5bd6636a92444fc48d
53 | core/autoware_msgs:
54 | type: git
55 | url: https://github.com/autowarefoundation/autoware_msgs.git
56 | version: 4f13d4b8b465ed7f424fce9af17882dbe1752875
57 | core/external/autoware_auto_msgs:
58 | type: git
59 | url: https://github.com/tier4/autoware_auto_msgs.git
60 | version: 6b5bc4365f9a2fc913bc11afa74ec21ffa2dbf32
61 | launcher/autoware_launch:
62 | type: git
63 | url: https://github.com/autowarefoundation/autoware_launch.git
64 | version: e4abe673667a8d4f2d783ed22edacbf5d4784b8f
65 | param/autoware_individual_params:
66 | type: git
67 | url: https://github.com/autowarefoundation/autoware_individual_params.git
68 | version: 79cff0ba014808050be6f5cb3b4764ba2c96c21c
69 | sensor_component/external/sensor_component_description:
70 | type: git
71 | url: https://github.com/tier4/sensor_component_description.git
72 | version: 475857daeb4c4883ab0295336713364b326e8278
73 | sensor_component/external/tamagawa_imu_driver:
74 | type: git
75 | url: https://github.com/tier4/tamagawa_imu_driver.git
76 | version: 28ad3cd4fb043e5f92353a540c3531cd4cb7bef3
77 | sensor_component/external/velodyne_vls:
78 | type: git
79 | url: https://github.com/tier4/velodyne_vls.git
80 | version: baeafaf9a376c5798f7b67a77211890c33900f84
81 | sensor_kit/external/awsim_sensor_kit_launch:
82 | type: git
83 | url: https://github.com/RobotecAI/awsim_sensor_kit_launch.git
84 | version: d9022ee9bbfd958c239b673cfbb230eea50607be
85 | sensor_kit/sample_sensor_kit_launch:
86 | type: git
87 | url: https://github.com/autowarefoundation/sample_sensor_kit_launch.git
88 | version: 03decbd31bb954eb9f52daaf3a3fa2b921dbb0c3
89 | universe/autoware.universe:
90 | type: git
91 | url: https://github.com/autowarefoundation/autoware.universe.git
92 | version: febbc135b8e09e993ed345ee6d3cd7e65b6c1d68
93 | universe/external/morai_msgs:
94 | type: git
95 | url: https://github.com/MORAI-Autonomous/MORAI-ROS2_morai_msgs.git
96 | version: 6fd6a711e4bbf8a9989b54028e8074acabbbce6f
97 | universe/external/muSSP:
98 | type: git
99 | url: https://github.com/tier4/muSSP.git
100 | version: c79e98fd5e658f4f90c06d93472faa977bc873b9
101 | universe/external/ndt_omp:
102 | type: git
103 | url: https://github.com/tier4/ndt_omp.git
104 | version: f59e1667390fe66d72c5c3aa0b25385b5b6dd8cf
105 | universe/external/pointcloud_to_laserscan:
106 | type: git
107 | url: https://github.com/tier4/pointcloud_to_laserscan.git
108 | version: 948a4fca35dcb03c6c8fbfa610a686f7c919fe0b
109 | universe/external/tier4_ad_api_adaptor:
110 | type: git
111 | url: https://github.com/tier4/tier4_ad_api_adaptor.git
112 | version: 5084f9c8eaf03458a216060798da2b1e4fa96f28
113 | universe/external/tier4_autoware_msgs:
114 | type: git
115 | url: https://github.com/tier4/tier4_autoware_msgs.git
116 | version: a360ee9f5235a0d426427813f26e43027e32139d
117 | vehicle/external/pacmod_interface:
118 | type: git
119 | url: https://github.com/tier4/pacmod_interface.git
120 | version: b5ae20345f2551da0c6e4140a3dc3479d64efd1f
121 | vehicle/sample_vehicle_launch:
122 | type: git
123 | url: https://github.com/autowarefoundation/sample_vehicle_launch.git
124 | version: 157238ca77de7b0a59f71a0b28f456741fab3ca2
125 | v2x/autowarev2x:
126 | type: git
127 | url: https://github.com/tlab-wide/AutowareV2X.git
128 | version: 48a1f2d3db6ae59e92febb93aad7cde760f4f3ec
129 | v2x/vanetza:
130 | type: git
131 | url: https://github.com/yuasabe/vanetza.git
132 | version: cfffe9afda177297c59bbb804d3e8f66120c8453
133 | ```
134 |
135 | !!! Note
136 | If you want to follow the latest ver, edit the `autoware.repos` file and add the following two repositories to the end.
137 |
138 | ```
139 | v2x/autowarev2x:
140 | type: git
141 | url: https://github.com/tlab-wide/AutowareV2X.git
142 | version: cpm-tr
143 | v2x/vanetza:
144 | type: git
145 | url: https://github.com/yuasabe/vanetza.git
146 | version: master
147 |
148 | ```
149 |
150 | 2. Update the repository
151 | ```
152 | vcs import src < autoware.repos
153 | vcs pull src
154 | ```
155 |
156 | 3. Install dependent ROS packages
157 | ```bash
158 | source /opt/ros/galactic/setup.bash
159 | rosdep install -y --from-paths src --ignore-src --rosdistro $ROS_DISTRO
160 | ```
161 |
162 | 4. Build the workspace
163 | ```
164 | colcon build --symlink-install --cmake-args -DCMAKE_BUILD_TYPE=Release
165 | ```
166 |
--------------------------------------------------------------------------------
/docs/tutorials/planning-simulation/index.md:
--------------------------------------------------------------------------------
1 | # Running AutowareV2X in the Planning Simulator
2 |
3 | Simulations can be an easy way of verifying the functionality of AutowareV2X before an actual field test.
4 |
5 | AutowareV2X can be run in a simulation environment using [Autoware's Planning Simulator](https://autowarefoundation.github.io/autoware-documentation/main/tutorials/ad-hoc-simulation/planning-simulation/). An ITS-S is composed of Autoware as the autonomous driving stack and AutowareV2X as its V2X communication stack. Each ITS-S is executed inside a Docker container, and the wireless communication medium between ITS-Ss are modeled with Docker networks. A dynamic ITS-S is defined as a CAV, while a static ITS-S is considered a RSU. Perceived Objects are sent out on the network as CPMs.
6 |
7 | !!! Note
8 | Make sure you have completed [Autoware's Planning Simulator Tutorial](https://autowarefoundation.github.io/autoware-documentation/main/tutorials/ad-hoc-simulation/planning-simulation/) before continuing.
9 |
10 | In order to test both the sender and receiver functionalities, we will need at least two ITS-S instances.
11 |
12 | ## The Docker environment to create
13 |
14 | We will be creating the Docker environment as depicted in the figure below. There will be two Docker containers to represent the two ITS-Ss, each of which includes Autoware.universe and AutowareV2X. They will both be a part of the Docker network called `v2x_net` with the subnet `10.0.0.0/24`. "Autoware Container #1" and "Autoware Container #2" will be described as `autoware_1` and `autoware_2` respectively.
15 |
16 | 
17 |
18 | ### Create a Docker network for V2X communication
19 |
20 | ```bash
21 | docker network create --driver=bridge --subnet=10.0.0.0/24 v2x_net -o com.docker.network.bridge.name="v2x_net"
22 | ```
23 |
24 |
25 | ### Launch two ITS-S containers
26 |
27 | !!! Note
28 | Here, we will use a Rocker extension called [off-your-rocker](https://github.com/sloretz/off-your-rocker).
29 | Install `off-your-rocker` by running the below:
30 | ```bash
31 | python3 -m pip install off-your-rocker
32 | ```
33 |
34 | In one terminal, use rocker to launch container `autoware_1`:
35 | ```bash
36 | rocker --nvidia --x11 --user --privileged --volume $HOME/workspace/autoware_docker --volume $HOME/data --network=v2x_net --name autoware_1 --oyr-run-arg "--ip 10.0.0.2 --hostname autoware_1" -- ghcr.io/autowarefoundation/autoware-universe:latest-cuda
37 | ```
38 |
39 | In another terminal, use rocker to launch container `autoware_2`:
40 | ```
41 | rocker --nvidia --x11 --user --privileged --volume $HOME/workspace/autoware_docker --volume $HOME/data --network=v2x_net --name autoware_2 --oyr-run-arg "--ip 10.0.0.3 --hostname autoware_2" -- ghcr.io/autowarefoundation/autoware-universe:latest-cuda
42 | ```
43 |
44 | ## Run Planning Simulator
45 |
46 | Run the Planning Simulator in both `autoware_1` and `autoware_2`.
47 |
48 | In `autoware_1`:
49 |
50 | ```
51 | cd ~/workspace/autoware_docker
52 | source install/setup.bash
53 | export AWID=1 # autoware_1
54 | source ~/workspace/autoware_docker/src/v2x/autowarev2x/setup.sh
55 | ros2 launch autoware_launch planning_simulator.launch.xml map_path:=$HOME/data/maps/sample-map-planning vehicle_model:=sample_vehicle sensor_model:=sample_sensor_kit
56 | ```
57 |
58 | Also, in `autoware_1`, set the ego-vehicle position by clicking `2D Pose Estimate`.
59 |
60 | Try adding some dummy cars by clicking `2D Dummy Car`.
61 | Note that you can make the dummy cars to be static by changing its `Velocity` to `0` in the `Tool Properties` pane.
62 |
63 | 
64 |
65 | 
66 |
67 | In `autoware_2`:
68 |
69 | ```
70 | cd ~/workspace/autoware_docker
71 | source install/setup.bash
72 | export AWID=2 # autoware_2
73 | source ~/workspace/autoware_docker/src/v2x/autowarev2x/setup.sh
74 | ros2 launch autoware_launch planning_simulator.launch.xml map_path:=$HOME/data/maps/sample-map-planning vehicle_model:=sample_vehicle sensor_model:=sample_sensor_kit
75 | ```
76 |
77 |
78 | ## Run AutowareV2X
79 |
80 | In another terminal, connect to the `autoware_1` and `autoware_2` containers, and start AutowareV2X in both of them. We will set `autoware_1` to be the CPM sender, and `autoware_2` to be the CPM receiver.
81 |
82 | In `autoware_1`:
83 | ```
84 | docker exec -it autoware_1 bash
85 | sudo su
86 | cd workspace/autoware_docker
87 | source install/setup.bash
88 | export AWID=1
89 | source ./src/v2x/autowarev2x/setup.sh
90 | ros2 launch autoware_v2x v2x.launch.xml network_interface:=eth0
91 | ```
92 |
93 | You should see the command output like below.
94 | It shows that you are "Sending CPM with n objects", and the `[objectsList]` line describes the following information: `cpm_num, objectID, object.uuid, object.to_send, object.to_send_trigger`.
95 | 
96 |
97 | In `autoware_2`:
98 | ```
99 | docker exec -it autoware_2 bash
100 | sudo su
101 | cd workspace/autoware_docker
102 | source install/setup.bash
103 | export AWID=2
104 | source ./src/v2x/autowarev2x/setup.sh
105 | ros2 launch autoware_v2x v2x.launch.xml network_interface:=eth0 is_sender:=false
106 | ```
107 |
108 | When both the sender and receiver is launched, you should see that the receiver (`autoware_2`) will start receiving CPMs like below.
109 |
110 | 
111 |
112 | ## Show CPM-shared objects in RViz
113 |
114 | 1. Press "Add" from the Displays Panel
115 | 
116 | 2. Choose "By topic", then select PredictedObjects from /v2x/cpm/objects
117 | 
118 | 3. The CPM-shared objects are shown in Rviz for `autoware_2`!
119 | 
120 |
121 | ## Run scenarios
122 |
123 | In order to run scenarios, the [scenario_simulator_v2](https://github.com/tier4/scenario_simulator_v2.git) must be installed:
124 |
125 | 1. Launch new Autoware container
126 | ```
127 | rocker --nvidia --x11 --user --volume $HOME/workspace/autoware_docker --volume $HOME/data -- ghcr.io/autowarefoundation/autoware-universe:latest-cuda
128 | ```
129 | 2. Add `simulator.repos`
130 | ```
131 | cd workspace/autoware_docker
132 | vcs import src < simulator.repos
133 | ```
134 | 3. Install dependent ROS packages
135 | ```
136 | sudo apt update
137 | rosdep update
138 | rosdep install --from-paths src --ignore-src --rosdistro $ROS_DISTRO -r
139 | ```
140 | 4. Rebuild workspace
141 | ```
142 | colcon build --symlink-install --cmake-args -DCMAKE_BUILD_TYPE=Release
143 | ```
144 | 5. Download scenario.
145 | ```bash
146 | gdown -O ~/data/scenarios/ 'https://drive.google.com/uc?id=1FXwSSWeFDTMz7qsG-J7pyJA6RgjksqCy'
147 | ```
148 | 6. Launch `scenario_test_runner` and specify scenario.
149 | ```
150 | ros2 launch scenario_test_runner scenario_test_runner.launch.py map_path:=$HOME/data/maps/sample-map-planning sensor_model:=sample_sensor_kit vehicle_model:=sample_vehicle scenario:=$HOME/data/scenarios/busy_kashiwa_scenario.yaml launch_autoware:=true
151 | ```
--------------------------------------------------------------------------------
/docs/installation/docker-installation.md:
--------------------------------------------------------------------------------
1 | # Docker Installation
2 |
3 | In order to run the simulations explained in the [Tutorials](/tutorials) section, you will need to proceed with the Docker installation.
4 |
5 | !!! Note
6 | Also refer to [Autoware's Docker Installation](https://autowarefoundation.github.io/autoware-documentation/main/installation/autoware/docker-installation/) for the Docker-based installation of Autoware.universe.
7 |
8 | ## Installing Autoware (Docker version)
9 |
10 | For the newest documentation for the Docker installation of Autoware, see their [official documentation](https://autowarefoundation.github.io/autoware-documentation/main/installation/autoware/docker-installation/).
11 |
12 | ### Prerequisites
13 | - git
14 |
15 | ### Setup
16 |
17 | 1. Prepare the repository.
18 | ```bash
19 | mkdir -p ~/workspace && cd ~/workspace
20 | git clone https://github.com/autowarefoundation/autoware.git autoware_docker
21 | cd autoware_docker
22 | ```
23 |
24 | 2. Run the setup script for docker installation.
25 | ```
26 | ./setup-dev-env.sh docker
27 | ```
28 | You will need to restart your PC after the script is finished running.
29 |
30 | 3. Make directory to store maps
31 | ```
32 | mkdir -p ~/data/maps
33 | ```
34 |
35 |
36 | ### Launch container
37 | ```
38 | # Launch Autoware container (with NVIDIA GPU)
39 | rocker --nvidia --x11 --user --privileged --volume $HOME/workspace/autoware_docker --volume $HOME/data -- ghcr.io/autowarefoundation/autoware-universe:latest-cuda
40 |
41 | # Launch Autoware container (without NVIDIA GPU)
42 | rocker -e LIBGL_ALWAYS_SOFTWARE=1 --x11 --user --privileged --volume $HOME/workspace/autoware_docker --volume $HOME/data -- ghcr.io/autowarefoundation/autoware-universe:latest-cuda
43 | ```
44 |
45 | ## Adding AutowareV2X
46 |
47 | !!! Note
48 | From here, run commands inside the container.
49 |
50 | 1. Move into `autoware_docker` directory.
51 | ```bash
52 | cd ~/workspace/autoware_docker
53 | ```
54 |
55 | 2. Edit the `autoware.repos` file and replace the following repositories.
56 | ```
57 | repositories:
58 | core/autoware.core:
59 | type: git
60 | url: https://github.com/autowarefoundation/autoware.core.git
61 | version: 6bafedfb24fb34157ed65bfe3f6f4c1ed0fbc80b
62 | core/autoware_adapi_msgs:
63 | type: git
64 | url: https://github.com/autowarefoundation/autoware_adapi_msgs.git
65 | version: 9679b5a7a1f4cfff2fa50b80d2759d3937f2f953
66 | core/autoware_common:
67 | type: git
68 | url: https://github.com/autowarefoundation/autoware_common.git
69 | version: 6916df26fafe6749db4b1d5bd6636a92444fc48d
70 | core/autoware_msgs:
71 | type: git
72 | url: https://github.com/autowarefoundation/autoware_msgs.git
73 | version: 4f13d4b8b465ed7f424fce9af17882dbe1752875
74 | core/external/autoware_auto_msgs:
75 | type: git
76 | url: https://github.com/tier4/autoware_auto_msgs.git
77 | version: 6b5bc4365f9a2fc913bc11afa74ec21ffa2dbf32
78 | launcher/autoware_launch:
79 | type: git
80 | url: https://github.com/autowarefoundation/autoware_launch.git
81 | version: e4abe673667a8d4f2d783ed22edacbf5d4784b8f
82 | param/autoware_individual_params:
83 | type: git
84 | url: https://github.com/autowarefoundation/autoware_individual_params.git
85 | version: 79cff0ba014808050be6f5cb3b4764ba2c96c21c
86 | sensor_component/external/sensor_component_description:
87 | type: git
88 | url: https://github.com/tier4/sensor_component_description.git
89 | version: 475857daeb4c4883ab0295336713364b326e8278
90 | sensor_component/external/tamagawa_imu_driver:
91 | type: git
92 | url: https://github.com/tier4/tamagawa_imu_driver.git
93 | version: 28ad3cd4fb043e5f92353a540c3531cd4cb7bef3
94 | sensor_component/external/velodyne_vls:
95 | type: git
96 | url: https://github.com/tier4/velodyne_vls.git
97 | version: baeafaf9a376c5798f7b67a77211890c33900f84
98 | sensor_kit/external/awsim_sensor_kit_launch:
99 | type: git
100 | url: https://github.com/RobotecAI/awsim_sensor_kit_launch.git
101 | version: d9022ee9bbfd958c239b673cfbb230eea50607be
102 | sensor_kit/sample_sensor_kit_launch:
103 | type: git
104 | url: https://github.com/autowarefoundation/sample_sensor_kit_launch.git
105 | version: 03decbd31bb954eb9f52daaf3a3fa2b921dbb0c3
106 | universe/autoware.universe:
107 | type: git
108 | url: https://github.com/autowarefoundation/autoware.universe.git
109 | version: febbc135b8e09e993ed345ee6d3cd7e65b6c1d68
110 | universe/external/morai_msgs:
111 | type: git
112 | url: https://github.com/MORAI-Autonomous/MORAI-ROS2_morai_msgs.git
113 | version: 6fd6a711e4bbf8a9989b54028e8074acabbbce6f
114 | universe/external/muSSP:
115 | type: git
116 | url: https://github.com/tier4/muSSP.git
117 | version: c79e98fd5e658f4f90c06d93472faa977bc873b9
118 | universe/external/ndt_omp:
119 | type: git
120 | url: https://github.com/tier4/ndt_omp.git
121 | version: f59e1667390fe66d72c5c3aa0b25385b5b6dd8cf
122 | universe/external/pointcloud_to_laserscan:
123 | type: git
124 | url: https://github.com/tier4/pointcloud_to_laserscan.git
125 | version: 948a4fca35dcb03c6c8fbfa610a686f7c919fe0b
126 | universe/external/tier4_ad_api_adaptor:
127 | type: git
128 | url: https://github.com/tier4/tier4_ad_api_adaptor.git
129 | version: 5084f9c8eaf03458a216060798da2b1e4fa96f28
130 | universe/external/tier4_autoware_msgs:
131 | type: git
132 | url: https://github.com/tier4/tier4_autoware_msgs.git
133 | version: a360ee9f5235a0d426427813f26e43027e32139d
134 | vehicle/external/pacmod_interface:
135 | type: git
136 | url: https://github.com/tier4/pacmod_interface.git
137 | version: b5ae20345f2551da0c6e4140a3dc3479d64efd1f
138 | vehicle/sample_vehicle_launch:
139 | type: git
140 | url: https://github.com/autowarefoundation/sample_vehicle_launch.git
141 | version: 157238ca77de7b0a59f71a0b28f456741fab3ca2
142 | v2x/autowarev2x:
143 | type: git
144 | url: https://github.com/tlab-wide/AutowareV2X.git
145 | version: 48a1f2d3db6ae59e92febb93aad7cde760f4f3ec
146 | v2x/vanetza:
147 | type: git
148 | url: https://github.com/yuasabe/vanetza.git
149 | version: cfffe9afda177297c59bbb804d3e8f66120c8453
150 | ```
151 |
152 | !!! Note
153 | If you want to follow the latest ver, edit the `autoware.repos` file and add the following two repositories to the end.
154 |
155 | ```
156 | v2x/autowarev2x:
157 | type: git
158 | url: https://github.com/tlab-wide/AutowareV2X.git
159 | version: cpm-tr
160 | v2x/vanetza:
161 | type: git
162 | url: https://github.com/yuasabe/vanetza.git
163 | version: master
164 |
165 | ```
166 |
167 | 3. Update the repository
168 | ```
169 | mkdir src
170 | vcs import src < autoware.repos
171 | vcs pull src
172 | ```
173 |
174 | 4. Install dependent ROS packages
175 | ```bash
176 | sudo apt update
177 | rosdep update
178 | rosdep install --from-paths . --ignore-src --rosdistro $ROS_DISTRO -r
179 | ```
180 |
181 | 5. Build the workspace
182 | ```
183 | colcon build --symlink-install --cmake-args -DCMAKE_BUILD_TYPE=Release
184 | ```
185 |
--------------------------------------------------------------------------------
/docs/tutorials/actual-devices/index.md:
--------------------------------------------------------------------------------
1 | # Running AutowareV2X on Actual Devices
2 |
3 | After testing functions in the simulator, it is time to install AutowareV2X on actual hardware.
4 |
5 | The setup that we consider is shown in the figure below. The Roadside Unit (RSU) acts as the CPM sender, and the CAV (Connected Autonomous Vehicle) is the CPM receiver.
6 | In terms of the software that comprises the RSU and CAV, they are both very similar.
7 |
8 | Take a look at the right hand side that shows the setup for the RSU. The sensing component for the RSU is a LiDAR (VLP-16) mounted on top of a tripod. The point cloud from the LiDAR is inputed into the Autoware PC with a normal Ethernet interface. The Autoware PC used here is an off-the-shelf gaming laptop. AutowareV2X is run on a separate machine, the Intel NUC, and it is connected to the Autoware PC via an Ethernet cable as well. All network interfaces, including the Wi-Fi module and 4G router in this case, is connected to the AutowareV2X PC. For the sake of simplicity, we will only focus on one Wi-Fi interface for this documentation.
9 |
10 | The set up for the vehicle is very similiar. The only difference is that the Autoware PC is now connected to the sensors and actuators of the vehicle.
11 |
12 | So, the **bare minimum for a CPM sender and receiver setup using AutowareV2X** is **TWO sets** of the following:
13 |
14 | - Autoware PC (Running the latest Autoware.universe)
15 | - AutowareV2X PC (Running the latest AutowareV2X)
16 | - LiDAR sensor (Autoware compatible, only necessary at the CPM Sender)
17 |
18 | In this tutorial, we will explain how to set this up. Depending on the number of CPM senders or receivers you need, you will need to make more copies of the same setup.
19 |
20 |
21 |
22 | ## Equipments
23 |
24 | The details for the equipment used in the setup is shown in the tables below.
25 |
26 | ### Hardware for the RSU
27 |
28 | | Purpose | Device | Specification |
29 | | ----------- | ----------- | ----------- |
30 | | AutowareV2X PC | Intel NUC, Model: 11Pro | OS: Ubuntu 20.04, ROS: Galactic, Software: AutowareV2X |
31 | | Autoware PC | Gtune PC, Model: H5| OS: Ubuntu 20.04, ROS: Galactic, Software: Autoware.universe |
32 | | Wi-Fi Network Interface | Netgear Wi-Fi Dongle, Model: A6210 | Dual-band: 802.11b/g/n (2.4GHz), 802.11a/n/ac (5GHz) |
33 | | 4G/LTE Network Interface | IDY IoM 5G Gateway, Model: iR730B | Bands: 3G, 4G, 5G, nano PSIM, 4x high-performance active antennas |
34 | | Sensor | VLP16 3D LiDAR | Range: 100 m, Accuracy: +/- 3 cm, Rotation rate: 5-20 Hz, 16 channels |
35 |
36 | ### Hardware for the CAV
37 |
38 | | Purpose | Device | Specification |
39 | | ----------- | ----------- | ----------- |
40 | | AutowareV2X PC | Intel NUC, Model: 11Pro | OS: Ubuntu 20.04, ROS: Galactic, Software: AutowareV2X |
41 | | Autoware PC | Gigabyte PC, Model: AERO-15 | OS: Ubuntu 20.04, ROS: Galactic, Software: Autoware.universe |
42 | | Wi-Fi Network Interface | Netgear Wi-Fi Dongle, Model: A6210 | Dual-band: 802.11b/g/n (2.4GHz), 802.11a/n/ac (5GHz) |
43 | | 4G/LTE Network Interface | IDY IoM 5G Gateway, Model: iR730B | Bands: 3G, 4G, 5G, nano PSIM, 4x high-performance active antennas |
44 | | Vehicle | Model: Yamaha G30Es-Li | Speed: less than 20 km/h, Sensor: VLP16 3D LiDAR (Rooftop) |
45 |
46 | ## Autoware PC
47 |
48 | 1. For the Autoware PC, you can follow the [normal installation procedures for Autoware.universe](https://autowarefoundation.github.io/autoware-documentation/main/installation/autoware/source-installation/). A PC with a discrete GPU is recommended for better performance with sensor processing and object detection.
49 |
50 | 2. Attach sensors to the Autoware PC (such as LiDARs) and make sure that you can start and run Autoware.
51 |
52 | 3. Confirm that the following two ROS2 topics are being published:
53 | - `/perception/object_recognition/objects`
54 | - `/tf`
55 |
56 | ## AutowareV2X PC
57 |
58 | ### Install AutowareV2X
59 |
60 | 1. Install Ubuntu 20.04 or 22.04 on a new PC.
61 |
62 | 2. Follow the [Source Installation Guide](../../installation/source-installation.md) for AutowareV2X to set up AutowareV2X. Do note that here, you will be installing Autoware again onto this PC, alongside AutowareV2X. But in practice, we will not be using the Autoware here and will rather use the Autoware on the Autoware PC.
63 |
64 | ### ROS2 Connectivity of Autoware PC and AutowareV2X PC
65 |
66 | 1. Connect the Autoware PC and AutowareV2X PC with an Ethernet cable (preferably Cat5e or above). Setup local IP addresses and IP routing on both the PCs so that they have IP reachability to each other. Confirm this by pinging each PC from the other.
67 |
68 | 2. Once this is setup, when you run Autoware on the Autoware PC, you should see all the ROS2 topics on the AutowareV2X PC as well. Confirm this by running `ros2 topic list` on the AutowareV2X PC after running Autoware or the Planning Simulator on the Autoware PC.
69 |
70 | !!! Note
71 | At this point, it may be wise to create at least two sets of the Autoware PC and AutowareV2X PC setup. One for the sender and the other for the receiver.
72 | ### Wi-Fi interface for CPM transmission
73 |
74 | 1. Attach a network interface to the AutowareV2X PC. You can use the default Wi-Fi interface or attach an USB dongle if necessary.
75 |
76 | 2. Create an Wi-Fi adhoc network to send the CPMs in. Sample commands are as below. Substitute the name of the network interface, and remember to change the IP address of the sender and receiver.
77 |
78 | ```
79 | sudo systemctl stop network-manager
80 | sudo ifconfig wlx94a67e5d6f4d down
81 | sudo iwconfig wlx94a67e5d6f4d mode ad-hoc
82 | sudo iwconfig wlx94a67e5d6f4d essid CITS-EXP
83 | sudo ip addr add 10.0.0.2/24 dev wlx94a67e5d6f4d
84 | sudo ifconfig wlx94a67e5d6f4d up
85 | ```
86 |
87 | 3. Once the adhoc network is up, you should be able to ping from the sender AutowareV2X PC to the receiver AutowareV2X PC if they are nearby.
88 |
89 | The `iwconfig` command can be used to check the adhoc network connection.
90 |
91 | ```bash
92 | # Sender AutowareV2X
93 | wlx94a67e5d6f4d IEEE 802.11 ESSID:"CITS-EXP"
94 | Mode:Ad-Hoc Frequency:2.412 GHz Cell: 0E:06:A2:BE:F5:6C
95 | Tx-Power=18 dBm
96 | Retry short limit:7 RTS thr:off Fragment thr:off
97 | Power Management:off
98 |
99 | # Receiver AutowareV2X
100 | wlx94a67e5d6f47 IEEE 802.11 ESSID:"CITS-EXP"
101 | Mode:Ad-Hoc Frequency:2.412 GHz Cell: 0E:06:A2:BE:F5:6C
102 | Tx-Power=18 dBm
103 | Retry short limit:7 RTS thr:off Fragment thr:off
104 | Power Management:off
105 | ```
106 |
107 | ## Run Autoware and AutowareV2X
108 |
109 | 1. Run Autoware on the Autoware PC.
110 |
111 | 2. At this point, if you run AutowareV2X as explained in the [Run AutowareV2X Tutorial](../planning-simulation/index.md#run-autowarev2x), you should see that AutowareV2X will generate CPMs from the object information it receives from the Autoware PC and send it out the network interface. `autoware_1` in the linked tutorial is the CPM Sender AutowareV2X PC, and `autoware_2` is the CPM Receiver AutowareV2X PC in this case.
112 |
113 | 3. Check that the CPMs can be received on the receiver side.
--------------------------------------------------------------------------------
/src/v2x_node.cpp:
--------------------------------------------------------------------------------
1 | #include "autoware_v2x/v2x_node.hpp"
2 | #include "autoware_v2x/v2x_app.hpp"
3 | #include "autoware_v2x/time_trigger.hpp"
4 | #include "autoware_v2x/router_context.hpp"
5 | #include "autoware_v2x/positioning.hpp"
6 | #include "autoware_v2x/security.hpp"
7 | #include "autoware_v2x/link_layer.hpp"
8 | #include "autoware_v2x/cpm_application.hpp"
9 |
10 | #include "rclcpp/rclcpp.hpp"
11 | #include "std_msgs/msg/string.hpp"
12 |
13 | #include
14 | #include
15 | #include
16 | #include
17 | #include
18 |
19 | #include "tf2/LinearMath/Quaternion.h"
20 | #include
21 | #include
22 | #include
23 |
24 | namespace gn = vanetza::geonet;
25 |
26 | using namespace vanetza;
27 | using namespace std::chrono;
28 |
29 | namespace v2x
30 | {
31 | V2XNode::V2XNode(const rclcpp::NodeOptions &node_options) : rclcpp::Node("autoware_v2x_node", node_options) {
32 | using std::placeholders::_1;
33 |
34 | objects_sub_ = this->create_subscription("/perception/object_recognition/objects", 10, std::bind(&V2XNode::objectsCallback, this, _1));
35 | tf_sub_ = this->create_subscription("/tf", 10, std::bind(&V2XNode::tfCallback, this, _1));
36 |
37 | cpm_objects_pub_ = create_publisher("/v2x/cpm/objects", rclcpp::QoS{10});
38 | // cpm_sender_pub_ = create_publisher("/v2x/cpm/sender", rclcpp::QoS{10});
39 |
40 | // Declare Parameters
41 | this->declare_parameter("network_interface", "vmnet1");
42 | this->declare_parameter("is_sender", true);
43 |
44 | // Launch V2XApp in a new thread
45 | app = new V2XApp(this);
46 | boost::thread v2xApp(boost::bind(&V2XApp::start, app));
47 |
48 | RCLCPP_INFO(get_logger(), "V2X Node Launched");
49 |
50 | // Make latency_log file from current timestamp
51 | time_t t = time(nullptr);
52 | const tm* lt = localtime(&t);
53 | std::stringstream s;
54 | s << "20" << lt->tm_year-100 <<"-" << lt->tm_mon+1 << "-" << lt->tm_mday << "_" << lt->tm_hour << ":" << lt->tm_min << ":" << lt->tm_sec;
55 | std::string timestamp = s.str();
56 | char cur_dir[1024];
57 | getcwd(cur_dir, 1024);
58 | std::string latency_log_filename = std::string(cur_dir) + "/latency_logs/latency_log_file_" + timestamp + ".csv";
59 | latency_log_file.open(latency_log_filename, std::ios::out);
60 | }
61 |
62 | void V2XNode::publishCpmSenderObject(double x_mgrs, double y_mgrs, double orientation) {
63 | autoware_auto_perception_msgs::msg::PredictedObjects cpm_sender_object_msg;
64 | std_msgs::msg::Header header;
65 | rclcpp::Time current_time = this->now();
66 | cpm_sender_object_msg.header.frame_id = "map";
67 | cpm_sender_object_msg.header.stamp = current_time;
68 |
69 | autoware_auto_perception_msgs::msg::PredictedObject object;
70 | autoware_auto_perception_msgs::msg::ObjectClassification classification;
71 | autoware_auto_perception_msgs::msg::Shape shape;
72 | autoware_auto_perception_msgs::msg::PredictedObjectKinematics kinematics;
73 |
74 | classification.label = autoware_auto_perception_msgs::msg::ObjectClassification::CAR;
75 | classification.probability = 0.99;
76 |
77 | shape.type = autoware_auto_perception_msgs::msg::Shape::BOUNDING_BOX;
78 | shape.dimensions.x = 5.0;
79 | shape.dimensions.y = 2.0;
80 | shape.dimensions.z = 1.7;
81 |
82 | kinematics.initial_pose_with_covariance.pose.position.x = x_mgrs;
83 | kinematics.initial_pose_with_covariance.pose.position.y = y_mgrs;
84 | kinematics.initial_pose_with_covariance.pose.position.z = 0.1;
85 |
86 | tf2::Quaternion quat;
87 | quat.setRPY(0, 0, orientation);
88 |
89 | kinematics.initial_pose_with_covariance.pose.orientation.x = quat.x();
90 | kinematics.initial_pose_with_covariance.pose.orientation.y = quat.y();
91 | kinematics.initial_pose_with_covariance.pose.orientation.z = quat.z();
92 | kinematics.initial_pose_with_covariance.pose.orientation.w = quat.w();
93 |
94 | object.classification.emplace_back(classification);
95 | object.shape = shape;
96 | object.kinematics = kinematics;
97 |
98 | cpm_sender_object_msg.objects.push_back(object);
99 |
100 | // publisher_v2x_cpm_sender_->publish(cpm_sender_object_msg);
101 |
102 | }
103 |
104 | void V2XNode::publishObjects(std::vector *objectsStack, int cpm_num) {
105 | autoware_auto_perception_msgs::msg::PredictedObjects output_dynamic_object_msg;
106 | std_msgs::msg::Header header;
107 | rclcpp::Time current_time = this->now();
108 | output_dynamic_object_msg.header.frame_id = "map";
109 | output_dynamic_object_msg.header.stamp = current_time;
110 |
111 | for (CpmApplication::Object obj : *objectsStack) {
112 | autoware_auto_perception_msgs::msg::PredictedObject object;
113 | autoware_auto_perception_msgs::msg::ObjectClassification classification;
114 | autoware_auto_perception_msgs::msg::Shape shape;
115 | autoware_auto_perception_msgs::msg::PredictedObjectKinematics kinematics;
116 |
117 | classification.label = autoware_auto_perception_msgs::msg::ObjectClassification::CAR;
118 | classification.probability = 0.99;
119 |
120 | shape.type = autoware_auto_perception_msgs::msg::Shape::BOUNDING_BOX;
121 | shape.dimensions.x = obj.shape_x / 10.0;
122 | shape.dimensions.y = obj.shape_y / 10.0;
123 | shape.dimensions.z = obj.shape_z / 10.0;
124 |
125 | kinematics.initial_pose_with_covariance.pose.position.x = obj.position_x;
126 | kinematics.initial_pose_with_covariance.pose.position.y = obj.position_y;
127 | kinematics.initial_pose_with_covariance.pose.position.z = 0.1;
128 |
129 | kinematics.initial_pose_with_covariance.pose.orientation.x = obj.orientation_x;
130 | kinematics.initial_pose_with_covariance.pose.orientation.y = obj.orientation_y;
131 | kinematics.initial_pose_with_covariance.pose.orientation.z = obj.orientation_z;
132 | kinematics.initial_pose_with_covariance.pose.orientation.w = obj.orientation_w;
133 |
134 | object.classification.emplace_back(classification);
135 | object.shape = shape;
136 | object.kinematics = kinematics;
137 |
138 | std::mt19937 gen(std::random_device{}());
139 | std::independent_bits_engine bit_eng(gen);
140 | std::generate(object.object_id.uuid.begin(), object.object_id.uuid.end(), bit_eng);
141 |
142 | output_dynamic_object_msg.objects.push_back(object);
143 | }
144 |
145 | std::chrono::milliseconds ms = std::chrono::duration_cast (
146 | std::chrono::system_clock::now().time_since_epoch()
147 | );
148 | latency_log_file << "T_publish," << cpm_num << "," << ms.count() << std::endl;
149 |
150 | cpm_objects_pub_->publish(output_dynamic_object_msg);
151 | }
152 |
153 | void V2XNode::objectsCallback(const autoware_auto_perception_msgs::msg::PredictedObjects::ConstSharedPtr msg) {
154 | rclcpp::Time msg_time = msg->header.stamp; // timestamp included in the Autoware Perception Msg.
155 |
156 | std::chrono::milliseconds ms = std::chrono::duration_cast (
157 | std::chrono::system_clock::now().time_since_epoch()
158 | );
159 | latency_log_file << "T_rosmsg,," << ms.count() << std::endl;
160 |
161 | app->objectsCallback(msg);
162 | }
163 |
164 | void V2XNode::tfCallback(const tf2_msgs::msg::TFMessage::ConstSharedPtr msg) {
165 | app->tfCallback(msg);
166 | }
167 | }
168 |
169 | #include "rclcpp_components/register_node_macro.hpp"
170 | RCLCPP_COMPONENTS_REGISTER_NODE(v2x::V2XNode)
--------------------------------------------------------------------------------
/src/cpm_application.cpp:
--------------------------------------------------------------------------------
1 | #include "autoware_v2x/cpm_application.hpp"
2 | #include "autoware_v2x/positioning.hpp"
3 | #include "autoware_v2x/security.hpp"
4 | #include "autoware_v2x/link_layer.hpp"
5 | #include "autoware_v2x/v2x_node.hpp"
6 |
7 | #include "tf2/LinearMath/Quaternion.h"
8 | #include "tf2/LinearMath/Matrix3x3.h"
9 |
10 | #include "rclcpp/rclcpp.hpp"
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 | #include
17 | #include
18 | #include
19 | #include
20 | #include
21 | #include
22 |
23 | #include
24 | #include
25 |
26 | #include
27 |
28 | #define _USE_MATH_DEFINES
29 | #include
30 |
31 | using namespace vanetza;
32 | using namespace std::chrono;
33 |
34 | namespace v2x {
35 | CpmApplication::CpmApplication(V2XNode *node, Runtime &rt, bool is_sender) :
36 | node_(node),
37 | runtime_(rt),
38 | ego_(),
39 | generationTime_(0),
40 | updating_objects_list_(false),
41 | sending_(false),
42 | is_sender_(is_sender),
43 | reflect_packet_(false),
44 | objectConfidenceThreshold_(0.0),
45 | include_all_persons_and_animals_(false),
46 | cpm_num_(0),
47 | received_cpm_num_(0),
48 | cpm_object_id_(0),
49 | use_dynamic_generation_rules_(false)
50 | {
51 | RCLCPP_INFO(node_->get_logger(), "CpmApplication started. is_sender: %d", is_sender_);
52 | set_interval(milliseconds(100));
53 | createTables();
54 | }
55 |
56 | void CpmApplication::set_interval(Clock::duration interval) {
57 | cpm_interval_ = interval;
58 | runtime_.cancel(this);
59 | schedule_timer();
60 | }
61 |
62 | void CpmApplication::schedule_timer() {
63 | runtime_.schedule(cpm_interval_, std::bind(&CpmApplication::on_timer, this, std::placeholders::_1), this);
64 | }
65 |
66 | void CpmApplication::on_timer(Clock::time_point) {
67 | schedule_timer();
68 | send();
69 | }
70 |
71 | CpmApplication::PortType CpmApplication::port() {
72 | return btp::ports::CPM;
73 | }
74 |
75 | std::string CpmApplication::uuidToHexString(const unique_identifier_msgs::msg::UUID &id) {
76 | std::stringstream ss;
77 | for (auto i = 0; i < 16; ++i) {
78 | ss << std::hex << std::setfill('0') << std::setw(2) << +id.uuid[i];
79 | }
80 | return ss.str();
81 | }
82 |
83 | void CpmApplication::indicate(const DataIndication &indication, UpPacketPtr packet) {
84 |
85 | asn1::PacketVisitor visitor;
86 | std::shared_ptr cpm = boost::apply_visitor(visitor, *packet);
87 |
88 | if (cpm) {
89 | RCLCPP_INFO(node_->get_logger(), "[INDICATE] Received CPM #%d", received_cpm_num_);
90 |
91 | rclcpp::Time current_time = node_->now();
92 | // RCLCPP_INFO(node_->get_logger(), "[CpmApplication::indicate] [measure] T_receive_r1 %ld", current_time.nanoseconds());
93 |
94 | asn1::Cpm message = *cpm;
95 | ItsPduHeader_t &header = message->header;
96 |
97 | std::chrono::milliseconds ms = std::chrono::duration_cast (
98 | std::chrono::system_clock::now().time_since_epoch()
99 | );
100 | node_->latency_log_file << "T_received," << header.stationID << "," << ms.count() << std::endl;
101 |
102 |
103 | // Calculate GDT and get GDT from CPM and calculate the "Age of CPM"
104 | TimestampIts_t gt_cpm = message->cpm.generationTime;
105 | // const auto time_now = duration_cast (runtime_.now().time_since_epoch());
106 | // uint16_t gdt = time_now.count();
107 | // int gdt_diff = (65536 + (gdt - gdt_cpm) % 65536) % 65536;
108 | // RCLCPP_INFO(node_->get_logger(), "[CpmApplication::indicate] [measure] GDT_CPM: %ld", gdt_cpm);
109 | // RCLCPP_INFO(node_->get_logger(), "[CpmApplication::indicate] [measure] GDT: %u", gdt);
110 | // RCLCPP_INFO(node_->get_logger(), "[CpmApplication::indicate] [measure] T_R1R2: %d", gdt_diff);
111 |
112 |
113 | CpmManagementContainer_t &management = message->cpm.cpmParameters.managementContainer;
114 | double lat = management.referencePosition.latitude / 1.0e7;
115 | double lon = management.referencePosition.longitude / 1.0e7;
116 |
117 | int zone;
118 | int grid_num_x = 4;
119 | int grid_num_y = 39;
120 | int grid_size = 100000;
121 | bool northp;
122 | double x, y;
123 |
124 | GeographicLib::UTMUPS::Forward(lat, lon, zone, northp, x, y);
125 | double x_mgrs = x - grid_num_x * grid_size;
126 | double y_mgrs = y - grid_num_y * grid_size;
127 |
128 | OriginatingVehicleContainer_t &ovc = message->cpm.cpmParameters.stationDataContainer->choice.originatingVehicleContainer;
129 |
130 | // Calculate ego-vehicle orientation (radians) from heading (degree).
131 | // orientation: True-East, counter-clockwise angle. (0.1 degree accuracy)
132 | int heading = ovc.heading.headingValue;
133 | double orientation = (90.0 - (double) heading / 10.0) * M_PI / 180.0;
134 | if (orientation < 0.0) orientation += (2.0 * M_PI);
135 | // double orientation = heading / 10.0;
136 | // RCLCPP_INFO(node_->get_logger(), "[CpmApplication::indicate] heading: %d", heading);
137 | // RCLCPP_INFO(node_->get_logger(), "[CpmApplication::indicate] orientation: %f", orientation);
138 |
139 |
140 | // Publish CPM Sender info to /v2x/cpm/sender through V2XNode function
141 | node_->publishCpmSenderObject(x_mgrs, y_mgrs, orientation);
142 |
143 |
144 | // Get PerceivedObjects
145 | receivedObjectsStack.clear();
146 |
147 | PerceivedObjectContainer_t *&poc = message->cpm.cpmParameters.perceivedObjectContainer;
148 |
149 | if (poc != NULL) {
150 | for (int i = 0; i < poc->list.count; ++i) {
151 | // RCLCPP_INFO(node_->get_logger(), "[INDICATE] Object: #%d", poc->list.array[i]->objectID);
152 |
153 | CpmApplication::Object object;
154 | double x1 = poc->list.array[i]->xDistance.value;
155 | double y1 = poc->list.array[i]->yDistance.value;
156 | x1 = x1 / 100.0;
157 | y1 = y1 / 100.0;
158 | object.position_x = x_mgrs + (cos(orientation) * x1 - sin(orientation) * y1);
159 | object.position_y = y_mgrs + (sin(orientation) * x1 + cos(orientation) * y1);
160 | object.shape_x = poc->list.array[i]->planarObjectDimension2->value;
161 | object.shape_y = poc->list.array[i]->planarObjectDimension1->value;
162 | object.shape_z = poc->list.array[i]->verticalObjectDimension->value;
163 |
164 | object.yawAngle = poc->list.array[i]->yawAngle->value;
165 | double yaw_radian = (M_PI * object.yawAngle / 10.0) / 180.0;
166 |
167 | tf2::Quaternion quat;
168 | quat.setRPY(0, 0, yaw_radian);
169 | object.orientation_x = quat.x();
170 | object.orientation_y = quat.y();
171 | object.orientation_z = quat.z();
172 | object.orientation_w = quat.w();
173 | // RCLCPP_INFO(node_->get_logger(), "[CpmApplication::indicate] object.quat: %f, %f, %f, %f", object.orientation_x, object.orientation_y, object.orientation_z, object.orientation_w);
174 |
175 | receivedObjectsStack.push_back(object);
176 | }
177 | node_->publishObjects(&receivedObjectsStack, header.stationID);
178 | } else {
179 | // RCLCPP_INFO(node_->get_logger(), "[INDICATE] Empty POC");
180 | }
181 |
182 | insertCpmToCpmTable(message, (char*) "cpm_received");
183 |
184 | if (reflect_packet_) {
185 | Application::DownPacketPtr packet{new DownPacket()};
186 | std::unique_ptr payload{new geonet::DownPacket()};
187 |
188 | payload->layer(OsiLayer::Application) = std::move(message);
189 |
190 | Application::DataRequest request;
191 | request.its_aid = aid::CP;
192 | request.transport_type = geonet::TransportType::SHB;
193 | request.communication_profile = geonet::CommunicationProfile::ITS_G5;
194 |
195 | Application::DataConfirm confirm = Application::request(request, std::move(payload), node_);
196 |
197 | if (!confirm.accepted()) {
198 | throw std::runtime_error("[CpmApplication::indicate] Packet reflection failed");
199 | }
200 | }
201 |
202 | ++received_cpm_num_;
203 |
204 | } else {
205 | RCLCPP_INFO(node_->get_logger(), "[INDICATE] Received broken content");
206 | }
207 | }
208 |
209 | void CpmApplication::updateMGRS(double *x, double *y) {
210 | ego_.mgrs_x = *x;
211 | ego_.mgrs_y = *y;
212 | // RCLCPP_INFO(node_->get_logger(), "[CpmApplication::updateMGRS] ego-vehicle.position: %.10f, %.10f", ego_.mgrs_x, ego_.mgrs_y);
213 | }
214 |
215 | void CpmApplication::updateRP(double *lat, double *lon, double *altitude) {
216 | ego_.latitude = *lat;
217 | ego_.longitude = *lon;
218 | ego_.altitude = *altitude;
219 | }
220 |
221 | void CpmApplication::updateGenerationTime(int *gdt, long *gdt_timestamp) {
222 | generationTime_ = *gdt;
223 | gdt_timestamp_ = *gdt_timestamp; // ETSI-epoch milliseconds timestamp
224 | }
225 |
226 | void CpmApplication::updateHeading(double *yaw) {
227 | ego_.heading = *yaw;
228 | }
229 |
230 | void CpmApplication::setAllObjectsOfPersonsAnimalsToSend(const autoware_auto_perception_msgs::msg::PredictedObjects::ConstSharedPtr msg) {
231 | if (msg->objects.size() > 0) {
232 | for (autoware_auto_perception_msgs::msg::PredictedObject obj : msg->objects) {
233 | std::string object_uuid = uuidToHexString(obj.object_id);
234 | auto found_object = std::find_if(objectsList.begin(), objectsList.end(), [&](auto const &e) {
235 | return !strcmp(e.uuid.c_str(), object_uuid.c_str());
236 | });
237 |
238 | if (found_object == objectsList.end()) {
239 |
240 | } else {
241 | found_object->to_send = true;
242 | }
243 | }
244 |
245 | }
246 | }
247 |
248 | void CpmApplication::updateObjectsList(const autoware_auto_perception_msgs::msg::PredictedObjects::ConstSharedPtr msg) {
249 | updating_objects_list_ = true;
250 |
251 | // Flag all objects as NOT_SEND
252 | if (objectsList.size() > 0) {
253 | for (auto& object : objectsList) {
254 | object.to_send = false;
255 | object.to_send_trigger = -1;
256 | }
257 | }
258 |
259 | if (msg->objects.size() > 0) {
260 | for (autoware_auto_perception_msgs::msg::PredictedObject obj : msg->objects) {
261 |
262 | // RCLCPP_INFO(node_->get_logger(), "%d", obj.classification.front().label);
263 | double existence_probability = obj.existence_probability;
264 | // RCLCPP_INFO(node_->get_logger(), "existence_probability: %f", existence_probability);
265 |
266 | std::string object_uuid = uuidToHexString(obj.object_id);
267 | // RCLCPP_INFO(node_->get_logger(), "received object_id: %s", object_uuid.c_str());
268 |
269 | // RCLCPP_INFO(node_->get_logger(), "ObjectsList count: %d", objectsList.size());
270 |
271 | if (existence_probability >= objectConfidenceThreshold_) {
272 | // ObjectConfidence > ObjectConfidenceThreshold
273 |
274 | // Object tracked in internal memory? (i.e. Is object included in ObjectsList?)
275 | auto found_object = std::find_if(objectsList.begin(), objectsList.end(), [&](auto const &e) {
276 | return !strcmp(e.uuid.c_str(), object_uuid.c_str());
277 | });
278 |
279 | if (found_object == objectsList.end()) {
280 | // Object is new to internal memory
281 |
282 | if (cpm_object_id_ > 255) {
283 | cpm_object_id_ = 0;
284 | }
285 |
286 | // Add new object to ObjectsList
287 | CpmApplication::Object object;
288 | object.objectID = cpm_object_id_;
289 | object.uuid = object_uuid;
290 | object.timestamp_ros = msg->header.stamp;
291 | object.position_x = obj.kinematics.initial_pose_with_covariance.pose.position.x;
292 | object.position_y = obj.kinematics.initial_pose_with_covariance.pose.position.y;
293 | object.position_z = obj.kinematics.initial_pose_with_covariance.pose.position.z;
294 | object.orientation_x = obj.kinematics.initial_pose_with_covariance.pose.orientation.x;
295 | object.orientation_y = obj.kinematics.initial_pose_with_covariance.pose.orientation.y;
296 | object.orientation_z = obj.kinematics.initial_pose_with_covariance.pose.orientation.z;
297 | object.orientation_w = obj.kinematics.initial_pose_with_covariance.pose.orientation.w;
298 | object.shape_x = std::lround(obj.shape.dimensions.x * 10.0);
299 | object.shape_y = std::lround(obj.shape.dimensions.y * 10.0);
300 | object.shape_z = std::lround(obj.shape.dimensions.z * 10.0);
301 |
302 | long long msg_timestamp_sec = msg->header.stamp.sec;
303 | long long msg_timestamp_nsec = msg->header.stamp.nanosec;
304 | msg_timestamp_sec -= 1072915200; // convert to etsi-epoch
305 | long long msg_timestamp_msec = msg_timestamp_sec * 1000 + msg_timestamp_nsec / 1000000;
306 | object.timeOfMeasurement = gdt_timestamp_ - msg_timestamp_msec;
307 | if (object.timeOfMeasurement < -1500 || object.timeOfMeasurement > 1500) {
308 | RCLCPP_INFO(node_->get_logger(), "[updateObjectsStack] timeOfMeasurement out of bounds: %d", object.timeOfMeasurement);
309 | continue;
310 | }
311 |
312 | object.to_send = true;
313 | object.to_send_trigger = 0;
314 | object.timestamp = runtime_.now();
315 |
316 | objectsList.push_back(object);
317 | ++cpm_object_id_;
318 |
319 | } else {
320 |
321 | // Object was already in internal memory
322 |
323 | // Object belongs to class person or animal
324 | if (obj.classification.front().label == autoware_auto_perception_msgs::msg::ObjectClassification::PEDESTRIAN || obj.classification.front().label == autoware_auto_perception_msgs::msg::ObjectClassification::UNKNOWN) {
325 |
326 | if (include_all_persons_and_animals_) {
327 | found_object->to_send = true;
328 | found_object->to_send_trigger = 5;
329 | }
330 |
331 | // Object has not been included in a CPM in the past 500 ms.
332 | if (runtime_.now().time_since_epoch().count() - found_object->timestamp.time_since_epoch().count() > 500000) {
333 | // Include all objects of class person or animal in the current CPM
334 | include_all_persons_and_animals_ = true;
335 | found_object->to_send = true;
336 | found_object->to_send_trigger = 5;
337 | setAllObjectsOfPersonsAnimalsToSend(msg);
338 | RCLCPP_INFO(node_->get_logger(), "Include all objects of person/animal class");
339 | }
340 |
341 | } else {
342 | // Object does not belong to class person or animal
343 |
344 | // Euclidean absolute distance has changed by more than 4m
345 | double dist = pow(obj.kinematics.initial_pose_with_covariance.pose.position.x - found_object->position_x, 2) + pow(obj.kinematics.initial_pose_with_covariance.pose.position.y - found_object->position_y, 2);
346 | dist = sqrt(dist);
347 | // RCLCPP_INFO(node_->get_logger(), "Distance changed: %f", dist);
348 | if (dist > 4) {
349 | found_object->to_send = true;
350 | found_object->to_send_trigger = 1;
351 | } else {
352 |
353 | }
354 |
355 | // Absolute speed changed by more than 0.5 m/s
356 | double speed = pow(obj.kinematics.initial_twist_with_covariance.twist.linear.x - found_object->twist_linear_x, 2) + pow(obj.kinematics.initial_twist_with_covariance.twist.linear.x- found_object->twist_linear_y, 2);
357 | speed = sqrt(speed);
358 | // RCLCPP_INFO(node_->get_logger(), "Speed changed: %f", dist);
359 | if (speed > 0.5) {
360 | found_object->to_send = true;
361 | found_object->to_send_trigger = 2;
362 | }
363 |
364 | // Orientation of speed vector changed by more than 4 degrees
365 | double twist_angular_x_diff = (obj.kinematics.initial_twist_with_covariance.twist.angular.x - found_object->twist_angular_x) * 180 / M_PI;
366 | double twist_angular_y_diff = (obj.kinematics.initial_twist_with_covariance.twist.angular.y - found_object->twist_angular_y) * 180 / M_PI;
367 | // RCLCPP_INFO(node_->get_logger(), "Orientation speed vector changed x: %f", twist_angular_x_diff);
368 | // RCLCPP_INFO(node_->get_logger(), "Orientation speed vector changed y: %f", twist_angular_y_diff);
369 | if( twist_angular_x_diff > 4 || twist_angular_y_diff > 4 ) {
370 | found_object->to_send = true;
371 | found_object->to_send_trigger = 3;
372 | }
373 |
374 |
375 | // It has been more than 1 s since last transmission of this object
376 | if (runtime_.now().time_since_epoch().count() - found_object->timestamp.time_since_epoch().count() > 1000000) {
377 | found_object->to_send = true;
378 | found_object->to_send_trigger = 4;
379 | // RCLCPP_INFO(node_->get_logger(), "Been more than 1s: %ld", runtime_.now().time_since_epoch().count() - found_object->timestamp.time_since_epoch().count());
380 | }
381 |
382 | }
383 |
384 | // Update found_object
385 | found_object->timestamp_ros = msg->header.stamp;
386 | found_object->position_x = obj.kinematics.initial_pose_with_covariance.pose.position.x;
387 | found_object->position_y = obj.kinematics.initial_pose_with_covariance.pose.position.y;
388 | found_object->position_z = obj.kinematics.initial_pose_with_covariance.pose.position.z;
389 | found_object->orientation_x = obj.kinematics.initial_pose_with_covariance.pose.orientation.x;
390 | found_object->orientation_y = obj.kinematics.initial_pose_with_covariance.pose.orientation.y;
391 | found_object->orientation_z = obj.kinematics.initial_pose_with_covariance.pose.orientation.z;
392 | found_object->orientation_w = obj.kinematics.initial_pose_with_covariance.pose.orientation.w;
393 | found_object->shape_x = std::lround(obj.shape.dimensions.x * 10.0);
394 | found_object->shape_y = std::lround(obj.shape.dimensions.y * 10.0);
395 | found_object->shape_z = std::lround(obj.shape.dimensions.z * 10.0);
396 |
397 | long long msg_timestamp_sec = msg->header.stamp.sec;
398 | long long msg_timestamp_nsec = msg->header.stamp.nanosec;
399 | msg_timestamp_sec -= 1072915200; // convert to etsi-epoch
400 | long long msg_timestamp_msec = msg_timestamp_sec * 1000 + msg_timestamp_nsec / 1000000;
401 | found_object->timeOfMeasurement = gdt_timestamp_ - msg_timestamp_msec;
402 | if (found_object->timeOfMeasurement < -1500 || found_object->timeOfMeasurement > 1500) {
403 | RCLCPP_INFO(node_->get_logger(), "[updateObjectsStack] timeOfMeasurement out of bounds: %d", found_object->timeOfMeasurement);
404 | continue;
405 | }
406 |
407 | found_object->timestamp = runtime_.now();
408 |
409 | // if use_dymanic_generation_rules_ == false, then always include object in CPM
410 | if (!use_dynamic_generation_rules_) {
411 | found_object->to_send = true;
412 | found_object->to_send_trigger = 0;
413 | }
414 |
415 | }
416 | }
417 | }
418 | } else {
419 | // No objects detected
420 | }
421 |
422 | // RCLCPP_INFO(node_->get_logger(), "ObjectsStack: %d objects", objectsStack.size());
423 | rclcpp::Time current_time = node_->now();
424 | // RCLCPP_INFO(node_->get_logger(), "[CpmApplication::updateObjectsStack] [measure] T_objstack_updated %ld", current_time.nanoseconds());
425 | updating_objects_list_ = false;
426 | }
427 |
428 | void CpmApplication::printObjectsList(int cpm_num) {
429 | // RCLCPP_INFO(node_->get_logger(), "------------------------");
430 | if (objectsList.size() > 0) {
431 | for (auto& object : objectsList) {
432 | RCLCPP_INFO(node_->get_logger(), "[objectsList] %d,%d,%s,%d,%d", cpm_num, object.objectID, object.uuid.c_str(), object.to_send, object.to_send_trigger);
433 | }
434 | } else {
435 | RCLCPP_INFO(node_->get_logger(), "[objectsList] %d,,,,", cpm_num);
436 | }
437 |
438 | // RCLCPP_INFO(node_->get_logger(), "------------------------");
439 | }
440 |
441 | void CpmApplication::send() {
442 |
443 | if (is_sender_) {
444 |
445 | sending_ = true;
446 |
447 | printObjectsList(cpm_num_);
448 |
449 | // RCLCPP_INFO(node_->get_logger(), "[CpmApplication::send] Sending CPM...");
450 |
451 | vanetza::asn1::Cpm message;
452 |
453 | // ITS PDU Header
454 | ItsPduHeader_t &header = message->header;
455 | header.protocolVersion = 1;
456 | header.messageID = 14;
457 | header.stationID = cpm_num_;
458 |
459 | CollectivePerceptionMessage_t &cpm = message->cpm;
460 |
461 | // Set GenerationTime
462 | RCLCPP_INFO(node_->get_logger(), "[CpmApplication::send] %ld", gdt_timestamp_);
463 | asn_long2INTEGER(&cpm.generationTime, (long) gdt_timestamp_);
464 |
465 | CpmManagementContainer_t &management = cpm.cpmParameters.managementContainer;
466 | management.stationType = StationType_passengerCar;
467 | management.referencePosition.latitude = ego_.latitude * 1e7;
468 | management.referencePosition.longitude = ego_.longitude * 1e7;
469 |
470 | StationDataContainer_t *&sdc = cpm.cpmParameters.stationDataContainer;
471 | sdc = vanetza::asn1::allocate();
472 | sdc->present = StationDataContainer_PR_originatingVehicleContainer;
473 |
474 | OriginatingVehicleContainer_t &ovc = sdc->choice.originatingVehicleContainer;
475 | ovc.speed.speedValue = 0;
476 | ovc.speed.speedConfidence = 1;
477 |
478 | // Calculate headingValue of ego-vehicle.
479 | // Convert ego-vehicle yaw to True-North clockwise angle (heading). 0.1 degree accuracy.
480 | int heading = std::lround(((-ego_.heading * 180.0 / M_PI) + 90.0) * 10.0);
481 | if (heading < 0) heading += 3600;
482 | ovc.heading.headingValue = heading;
483 | ovc.heading.headingConfidence = 1;
484 |
485 | int perceivedObjectsCount = 0;
486 |
487 | if (objectsList.size() > 0) {
488 | PerceivedObjectContainer_t *&poc = cpm.cpmParameters.perceivedObjectContainer;
489 | poc = vanetza::asn1::allocate();
490 |
491 | for (auto& object : objectsList) {
492 |
493 | // RCLCPP_INFO(node_->get_logger(), "object.to_send: %d", object.to_send);
494 |
495 | if (object.to_send) {
496 |
497 | PerceivedObject *pObj = vanetza::asn1::allocate();
498 |
499 | // Update CPM-specific values for Object
500 | object.xDistance = std::lround((
501 | (object.position_x - ego_.mgrs_x) * cos(-ego_.heading) - (object.position_y - ego_.mgrs_y) * sin(-ego_.heading)
502 | ) * 100.0);
503 | object.yDistance = std::lround((
504 | (object.position_x - ego_.mgrs_x) * sin(-ego_.heading) + (object.position_y - ego_.mgrs_y) * cos(-ego_.heading)
505 | ) * 100.0);
506 | if (object.xDistance < -132768 || object.xDistance > 132767) {
507 | RCLCPP_WARN(node_->get_logger(), "xDistance out of bounds. objectID: #%d", object.objectID);
508 | continue;
509 | }
510 | if (object.yDistance < -132768 || object.yDistance > 132767) {
511 | RCLCPP_WARN(node_->get_logger(), "yDistance out of bounds. objectID: #%d", object.objectID);
512 | continue;
513 | }
514 |
515 | // Calculate orientation of detected object
516 | tf2::Quaternion quat(object.orientation_x, object.orientation_y, object.orientation_z, object.orientation_w);
517 | tf2::Matrix3x3 matrix(quat);
518 | double roll, pitch, yaw;
519 | matrix.getRPY(roll, pitch, yaw);
520 | if (yaw < 0) {
521 | object.yawAngle = std::lround(((yaw + 2*M_PI) * 180.0 / M_PI) * 10.0); // 0 - 3600
522 | } else {
523 | object.yawAngle = std::lround((yaw * 180.0 / M_PI) * 10.0); // 0 - 3600
524 | }
525 | object.xSpeed = 0;
526 | object.ySpeed = 0;
527 | pObj->objectID = object.objectID;
528 | pObj->timeOfMeasurement = object.timeOfMeasurement;
529 | pObj->xDistance.value = object.xDistance;
530 | pObj->xDistance.confidence = 1;
531 | pObj->yDistance.value = object.yDistance;
532 | pObj->yDistance.confidence = 1;
533 | pObj->xSpeed.value = object.xSpeed;
534 | pObj->xSpeed.confidence = 1;
535 | pObj->ySpeed.value = object.ySpeed;
536 | pObj->ySpeed.confidence = 1;
537 |
538 | pObj->planarObjectDimension1 = vanetza::asn1::allocate();
539 | pObj->planarObjectDimension2 = vanetza::asn1::allocate();
540 | pObj->verticalObjectDimension = vanetza::asn1::allocate();
541 |
542 | (*(pObj->planarObjectDimension1)).value = object.shape_y;
543 | (*(pObj->planarObjectDimension1)).confidence = 1;
544 | (*(pObj->planarObjectDimension2)).value = object.shape_x;
545 | (*(pObj->planarObjectDimension2)).confidence = 1;
546 | (*(pObj->verticalObjectDimension)).value = object.shape_z;
547 | (*(pObj->verticalObjectDimension)).confidence = 1;
548 |
549 | pObj->yawAngle = vanetza::asn1::allocate();
550 | (*(pObj->yawAngle)).value = object.yawAngle;
551 | (*(pObj->yawAngle)).confidence = 1;
552 |
553 | ASN_SEQUENCE_ADD(poc, pObj);
554 |
555 | // object.to_send = false;
556 | // object.to_send_trigger = -1;
557 | // RCLCPP_INFO(node_->get_logger(), "Sending object: %s", object.uuid.c_str());
558 |
559 | ++perceivedObjectsCount;
560 |
561 | } else {
562 | // Object.to_send is set to False
563 | // RCLCPP_INFO(node_->get_logger(), "Object: %s not being sent.", object.uuid.c_str());
564 | }
565 | }
566 |
567 | cpm.cpmParameters.numberOfPerceivedObjects = perceivedObjectsCount;
568 |
569 | if (perceivedObjectsCount == 0) {
570 | cpm.cpmParameters.perceivedObjectContainer = NULL;
571 | }
572 |
573 | } else {
574 | cpm.cpmParameters.perceivedObjectContainer = NULL;
575 | RCLCPP_INFO(node_->get_logger(), "[CpmApplication::send] Empty POC");
576 | }
577 |
578 | RCLCPP_INFO(node_->get_logger(), "[CpmApplication::send] Sending CPM with %d objects", perceivedObjectsCount);
579 |
580 | insertCpmToCpmTable(message, (char*) "cpm_sent");
581 |
582 | std::unique_ptr payload{new geonet::DownPacket()};
583 |
584 | payload->layer(OsiLayer::Application) = std::move(message);
585 |
586 | Application::DataRequest request;
587 | request.its_aid = aid::CP;
588 | request.transport_type = geonet::TransportType::SHB;
589 | request.communication_profile = geonet::CommunicationProfile::ITS_G5;
590 |
591 | Application::DataConfirm confirm = Application::request(request, std::move(payload), node_);
592 |
593 | if (!confirm.accepted()) {
594 | throw std::runtime_error("[CpmApplication::send] CPM application data request failed");
595 | }
596 |
597 | sending_ = false;
598 | // rclcpp::Time current_time = node_->now();
599 | // RCLCPP_INFO(node_->get_logger(), "[CpmApplication::send] [measure] T_depart_r1 %ld", current_time.nanoseconds());
600 |
601 | std::chrono::milliseconds ms = std::chrono::duration_cast (
602 | std::chrono::system_clock::now().time_since_epoch()
603 | );
604 | node_->latency_log_file << "T_depart," << cpm_num_ << "," << ms.count() << std::endl;
605 |
606 | ++cpm_num_;
607 | }
608 | }
609 |
610 | void CpmApplication::createTables() {
611 | sqlite3 *db = NULL;
612 | char* err = NULL;
613 |
614 | int ret = sqlite3_open("autoware_v2x.db", &db);
615 | if (ret != SQLITE_OK) {
616 | RCLCPP_INFO(node_->get_logger(), "DB File Open Error");
617 | return;
618 | }
619 |
620 | char* sql_command;
621 |
622 | sql_command = (char*) "create table if not exists cpm_sent(id INTEGER PRIMARY KEY, timestamp BIGINT, perceivedObjectCount INTEGER);";
623 |
624 | ret = sqlite3_exec(db, sql_command, NULL, NULL, &err);
625 | if (ret != SQLITE_OK) {
626 | RCLCPP_INFO(node_->get_logger(), "DB Execution Error (create table cpm_sent)");
627 | sqlite3_close(db);
628 | sqlite3_free(err);
629 | return;
630 | }
631 |
632 | sql_command = (char*) "create table if not exists cpm_received(id INTEGER PRIMARY KEY, timestamp BIGINT, perceivedObjectCount INTEGER);";
633 |
634 | ret = sqlite3_exec(db, sql_command, NULL, NULL, &err);
635 | if (ret != SQLITE_OK) {
636 | RCLCPP_INFO(node_->get_logger(), "DB Execution Error (create table cpm_received)");
637 | sqlite3_close(db);
638 | sqlite3_free(err);
639 | return;
640 | }
641 |
642 | sqlite3_close(db);
643 | RCLCPP_INFO(node_->get_logger(), "CpmApplication::createTables Finished");
644 | }
645 |
646 | void CpmApplication::insertCpmToCpmTable(vanetza::asn1::Cpm cpm, char* table_name) {
647 | sqlite3 *db = NULL;
648 | char* err = NULL;
649 |
650 | int ret = sqlite3_open("autoware_v2x.db", &db);
651 | if (ret != SQLITE_OK) {
652 | RCLCPP_INFO(node_->get_logger(), "DB File Open Error");
653 | return;
654 | }
655 |
656 | int64_t timestamp = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count();
657 |
658 | int perceivedObjectCount = 0;
659 | if (cpm->cpm.cpmParameters.numberOfPerceivedObjects) {
660 | perceivedObjectCount = cpm->cpm.cpmParameters.numberOfPerceivedObjects;
661 | }
662 |
663 | std::stringstream sql_command;
664 |
665 | sql_command << "insert into " << table_name << " (timestamp, perceivedObjectCount) values (" << timestamp << ", " << perceivedObjectCount << ");";
666 |
667 | ret = sqlite3_exec(db, sql_command.str().c_str(), NULL, NULL, &err);
668 | if (ret != SQLITE_OK) {
669 | RCLCPP_INFO(node_->get_logger(), "DB Execution Error (insertCpmToCpmTable)");
670 | RCLCPP_INFO(node_->get_logger(), sql_command.str().c_str());
671 | RCLCPP_INFO(node_->get_logger(), err);
672 | sqlite3_close(db);
673 | sqlite3_free(err);
674 | return;
675 | }
676 |
677 | sqlite3_close(db);
678 | // RCLCPP_INFO(node_->get_logger(), "CpmApplication::insertCpmToCpmTable Finished");
679 | }
680 | }
--------------------------------------------------------------------------------