参考资料:https://blog.csdn.net/zxh1592000/article/details/109095605?spm=1001.2101.3001.6650.1&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-2-109095605-blog-125250141.pc_relevant_3mothn_strategy_and_data_recovery&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-2-109095605-blog-125250141.pc_relevant_3mothn_strategy_and_data_recovery&utm_relevant_index=2
整体代码就分为客户端、通信接口、服务端三部分。
客户端代码在PythonApi文件夹,主要形式是由c++通过boost::python封装实现的的pythonapi。
通信代码在LibCarla文件夹,主要是通过c++的rpc库实现客户端和server的通信。carla的通信使用的是c++单独的rpc库,而不是使用的ue4的rpc通信机制。
服务端代码在unreal文件夹,这部分与其他由c++开发的ue4项目的代码类似。
这里的客户端代码特指实现基础功能(生成车辆、设置油门/方向),不包括carla在例程中给出的示例应用程序和算法。
carla是基于ue4的服务器-客户端架构实现的,所有计算都是在服务端实现的,客户端只负责发出请求、接收响应(这种说法并不严谨,有小部分简单功能的计算是在本地实现的)。所以客户端的程序要么是调用通信接口与服务器通信,要么是接收响应后在本地输出。
下面这个代码是D:\carla-master\LibCarla\source\carla\client\Actor.cpp的一部分,如果对carla的pythonapi熟悉的化就会对其中的这些名字比较熟悉。这里调用了boost::python库,将后面的c++函数封装成python函数,最后测成一个python类
class_<cc::Vehicle, bases<cc::Actor>, boost::noncopyable, boost::shared_ptr<cc::Vehicle>>("Vehicle",
no_init)
.def("apply_control", &cc::Vehicle::ApplyControl, (arg("control")))
.def("get_control", &cc::Vehicle::GetControl)
.def("set_light_state", &cc::Vehicle::SetLightState, (arg("light_state")))
.def("open_door", &cc::Vehicle::OpenDoor, (arg("door_idx")))
.def("close_door", &cc::Vehicle::CloseDoor, (arg("door_idx")))
.def("set_wheel_steer_direction", &cc::Vehicle::SetWheelSteerDirection, (arg("wheel_location")), (arg("angle_in_deg")))
.def("get_wheel_steer_angle", &cc::Vehicle::GetWheelSteerAngle, (arg("wheel_location")))
.def("get_light_state", CONST_CALL_WITHOUT_GIL(cc::Vehicle, GetLightState))
.def("apply_physics_control", &cc::Vehicle::ApplyPhysicsControl, (arg("physics_control")))
.def("get_physics_control", CONST_CALL_WITHOUT_GIL(cc::Vehicle, GetPhysicsControl))
.def("set_autopilot", CALL_WITHOUT_GIL_2(cc::Vehicle, SetAutopilot, bool, uint16_t), (arg("enabled") = true, arg("tm_port") = ctm::TM_DEFAULT_PORT))
.def("show_debug_telemetry", &cc::Vehicle::ShowDebugTelemetry, (arg("enabled") = true))
.def("get_speed_limit", &cc::Vehicle::GetSpeedLimit)
.def("get_traffic_light_state", &cc::Vehicle::GetTrafficLightState)
.def("is_at_traffic_light", &cc::Vehicle::IsAtTrafficLight)
.def("get_traffic_light", &cc::Vehicle::GetTrafficLight)
.def("enable_carsim", &cc::Vehicle::EnableCarSim, (arg("simfile_path") = ""))
.def("use_carsim_road", &cc::Vehicle::UseCarSimRoad, (arg("enabled")))
.def("enable_chrono_physics", &cc::Vehicle::EnableChronoPhysics, (arg("max_substeps")=30, arg("max_substep_delta_time")=0.002, arg("vehicle_json")="", arg("powetrain_json")="", arg("tire_json")="", arg("base_json_path")=""))
.def(self_ns::str(self_ns::self))
;
以apply_control为例,这个函数的功能是给出新油门和方向盘的数据。一路“转到定义”,最后发现这个函数在D:\carla-master\LibCarla\source\carla\client\detail\client.cpp文件中。
通信使用的是rpc,这里就是最后通信部分的代码
未完待续。。。
如果之前有按照carla的官方文档实现过添加自定义传感器,那么对这里应该会熟悉一点。
这块的文件在D:\carla-master\Unreal\CarlaUE4\Plugins\Carla\Source\Carla\Server\CarlaServer.cpp,代码如下:
// ~~ Apply control ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
BIND_SYNC(apply_control_to_vehicle) << [this](
cr::ActorId ActorId,
cr::VehicleControl Control) -> R<void>
{
REQUIRE_CARLA_EPISODE();
FCarlaActor* CarlaActor = Episode->FindCarlaActor(ActorId);
if (!CarlaActor)
{
return RespondError(
"apply_control_to_vehicle",
ECarlaServerResponse::ActorNotFound,
" Actor Id: " + FString::FromInt(ActorId));
}
ECarlaServerResponse Response =
CarlaActor->ApplyControlToVehicle(Control, EVehicleInputPriority::Client);
if (Response != ECarlaServerResponse::Success)
{
return RespondError(
"apply_control_to_vehicle",
Response,
" Actor Id: " + FString::FromInt(ActorId));
}
return R<void>::Success();
};
这里的关键就是调用了ApplyControlToVehicle(),这个函数是虚函数,真正的实现在D:\carla-master\Unreal\CarlaUE4\Plugins\Carla\Source\Carla\Actor\CarlaActor.cpp,代码如下:
ECarlaServerResponse FVehicleActor::ApplyControlToVehicle(
const FVehicleControl& Control, const EVehicleInputPriority& Priority)
{
if (IsDormant())
{
FVehicleData* ActorData = GetActorData<FVehicleData>();
ActorData->Control = Control;
}
else
{
auto Vehicle = Cast<ACarlaWheeledVehicle>(GetActor());
if (Vehicle == nullptr)
{
return ECarlaServerResponse::NotAVehicle;
}
Vehicle->ApplyVehicleControl(Control, Priority);
}
return ECarlaServerResponse::Success;
}
再次跳转到ApplyVehicleControl()的定义,代码如下:
UFUNCTION(Category = "CARLA Wheeled Vehicle", BlueprintCallable)
void ApplyVehicleControl(const FVehicleControl &Control, EVehicleInputPriority Priority)
{
if (InputControl.Priority <= Priority)
{
InputControl.Control = Control;
InputControl.Priority = Priority;
}
}
如果做过ue4开发对这里就很熟悉了,就是直接设置相关参数。