Carla采用的是CS的架構(gòu)雷则,即
- Server端是在UE4當(dāng)中,作為UE4的一個插件Plugin
- Client端是C++客戶端或者是Python客戶端
- 中間通過rpc框架進(jìn)行通信褐望,走的是TCP協(xié)議
首先來看一張很重要的圖
這張圖清晰明了的說明了Carla的整體框架,接下來我們一個一個分析
RPC框架
建議讀者先要理解RPC框架才往后閱讀
Carla所使用的是rpc框架是rpclib
袖扛,可以在github上找到:地址
或者在Build/rpclib-src
目錄下
LibCarla
LibCarla是Carla的核心代碼C++實現(xiàn)拦耐,提供給Server
端和Client
端使用,同時對rpclib
進(jìn)行了封裝榕暇,具體目錄在LibCarla\source\carla
下蓬衡,其中LibCarla\source\third-party
則是Carla所使用的第三方庫
我們可以在LibCarla\cmake
目錄中看到以下目錄結(jié)構(gòu)喻杈,說明Server
端和Client
端是分開構(gòu)建的
-
Server
端依賴的代碼在Carla構(gòu)建完之后會被安裝到Unreal\CarlaUE4\Plugins\Carla\CarlaDependencies
目錄下 -
Client
端依賴的代碼在Carla構(gòu)建完之后會被安裝到PythonAPI\carla\dependencies
目錄下
Server端
Server端的代碼在Unreal/CarlaUE4/Plugins/Carla/Source/Carla
目錄下,其中Server/CarlaServer.cpp
里包含了Carla對rpc::Server
的一個封裝
class ServerBinder
{
public:
constexpr ServerBinder(const char *name, carla::rpc::Server &srv, bool sync)
: _name(name),
_server(srv),
_sync(sync) {}
template <typename FuncT>
auto operator<<(FuncT func)
{
if (_sync)
{
_server.BindSync(_name, func);
}
else
{
_server.BindAsync(_name, func);
}
return func;
}
private:
const char *_name;
carla::rpc::Server &_server;
bool _sync;
};
#define BIND_SYNC(name) auto name = ServerBinder(# name, Server, true)
#define BIND_ASYNC(name) auto name = ServerBinder(# name, Server, false)
// =============================================================================
// -- Bind Actions -------------------------------------------------------------
// =============================================================================
void FCarlaServer::FPimpl::BindActions()
{
namespace cr = carla::rpc;
namespace cg = carla::geom;
/// Looks for a Traffic Manager running on port
BIND_SYNC(is_traffic_manager_running) << [this] (uint16_t port) ->R<bool>
{
return (TrafficManagerInfo.find(port) != TrafficManagerInfo.end());
};
// ... 其余代碼
}
通過源碼可以看到狰晚,BIND_SYNC
和BIND_ASYNC
兩個宏實現(xiàn)了Server端函數(shù)調(diào)用的綁定筒饰,例如:is_traffic_manager_running
函數(shù)
Client端(C++)
我們可以在LibCarla\source\carla\client\detail\Client.cpp
中找到Client端的實現(xiàn)代碼,不過如果你要編寫的是C++的Client的話壁晒,你可以從PythonAPI\carla\dependencies
目錄下拿取安裝好的
class Client::Pimpl {
public:
Pimpl(const std::string &host, uint16_t port, size_t worker_threads)
: endpoint(host + ":" + std::to_string(port)),
rpc_client(host, port),
streaming_client(host) {
rpc_client.set_timeout(5000u);
streaming_client.AsyncRun(
worker_threads > 0u ? worker_threads : std::thread::hardware_concurrency());
}
template <typename ... Args>
auto RawCall(const std::string &function, Args && ... args) {
try {
return rpc_client.call(function, std::forward<Args>(args) ...);
} catch (const ::rpc::timeout &) {
throw_exception(TimeoutException(endpoint, GetTimeout()));
}
}
template <typename T, typename ... Args>
auto CallAndWait(const std::string &function, Args && ... args) {
auto object = RawCall(function, std::forward<Args>(args) ...);
using R = typename carla::rpc::Response<T>;
auto response = object.template as<R>();
if (response.HasError()) {
throw_exception(std::runtime_error(response.GetError().What()));
}
return Get(response);
}
template <typename ... Args>
void AsyncCall(const std::string &function, Args && ... args) {
// Discard returned future.
rpc_client.async_call(function, std::forward<Args>(args) ...);
}
time_duration GetTimeout() const {
auto timeout = rpc_client.get_timeout();
DEBUG_ASSERT(timeout.has_value());
return time_duration::milliseconds(static_cast<size_t>(*timeout));
}
const std::string endpoint;
rpc::Client rpc_client;
streaming::Client streaming_client;
};
Client::Client(
const std::string &host,
const uint16_t port,
const size_t worker_threads)
: _pimpl(std::make_unique<Pimpl>(host, port, worker_threads)) {}
bool Client::IsTrafficManagerRunning(uint16_t port) const {
return _pimpl->CallAndWait<bool>("is_traffic_manager_running", port);
}
通過源碼可以看到瓷们,Client端調(diào)用了Server端的函數(shù)is_traffic_manager_running
Client端(Python)
目錄:PythonAPI\carla\source\libcarla
,主要是通過boost::python
來實現(xiàn)C++到Python的綁定
我們隨便看一個文件秒咐,例如PythonAPI\carla\source\libcarla\Actor.cpp
文件谬晕,具體的綁定用法需要讀者自己去了解boost::python