大家好,我已经把CSDN上的博客迁移到了知乎上,欢迎大家在知乎关注我的专栏慢慢悠悠小马车https://zhuanlan.zhihu.com/duangduangduang。希望大家可以多多交流,互相学习。


接触CARLA有近一个月的时间了,主要实现了在其中控制车辆沿规划的轨迹行驶。我使用CARLA的目的在于验证自己的决策规划算法是否正确,此处暂时不提决策规划的内容,只说如何将轨迹输入到CARLA仿真环境中。

我采用的方式是决策规划模块输出的轨迹封装成ROS Message的形式发布出来,将CARLA/PythonAPI/examples/automatic_control.py改成一个订阅该topic的节点,然后周期性的将轨迹传送到CARLA server端。server端有AWheeledVehicleAIController 与自动驾驶模式对应,并且在这个类中有SetFixedRoute()函数可以设定自车的行驶轨迹。可惜的是,PythonAPI中并没有这个函数,那么,就需要自定义API啦。定义API、打通server和client端的联系,套路对于每个函数都是一样的,这里以SetAutopilot(bool Enable)函数为例,请自行补充默认参数设置和头文件的相关声明。

  1. 在AWheeledVehicleAIController(或其他位置)中定义好SetAutopilot(bool Enable)的具体行为;

  2. 在CarlaServer.cpp中

    BIND_SYNC(set_actor_autopilot) << [this](
          cr::ActorId ActorId,
          bool bEnabled) -> R<void>
      {
        REQUIRE_CARLA_EPISODE();
        auto ActorView = Episode->FindActor(ActorId);
        if (!ActorView.IsValid())
        {
          RESPOND_ERROR("unable to set autopilot: actor not found");
        }
        auto Vehicle = Cast<ACarlaWheeledVehicle>(ActorView.GetActor());
        if (Vehicle == nullptr)
        {
          RESPOND_ERROR("unable to set autopilot: actor does not support autopilot");
        }
        auto Controller = Cast<AWheeledVehicleAIController>(Vehicle->GetController());
        if (Controller == nullptr)
        {
          RESPOND_ERROR("unable to set autopilot: vehicle controller does not support autopilot");
        }
        Controller->SetAutopilot(bEnabled);
        return R<void>::Success();
      };

     

  3.  在Client.cpp中

    void Client::SetActorAutopilot(rpc::ActorId vehicle, const bool enabled) {
        _pimpl->AsyncCall("set_actor_autopilot", vehicle, enabled);
      }

     

  4. 在 Simulator.h中

    void SetVehicleAutopilot(Vehicle &vehicle, bool enabled = true) {
          _client.SetActorAutopilot(vehicle.GetId(), enabled);
        }

     

  5. 在Vehicle.cpp中

    void Vehicle::SetAutopilot(bool enabled) {
        GetEpisode().Lock()->SetVehicleAutopilot(*this, enabled);
      }

     

  6. 在Actor.cpp中

    .def("set_autopilot", &cc::Vehicle::SetAutopilot, (arg("enabled") = true))

     

  7. 在PythonAPI中就可以调用set_autopilot()函数了。

    world.player.set_autopilot(True)

     

非容器(stl的vector、map,Python的list等)类参数按上述操作即可成功,但是对于vector、list参数的传递和解析,一直没有成功。希望有经验的同学可以教我。于是,我采用了逐点设置的方式设定轨迹,这里注意每点之间要加延时(>100us),否则client设定的顺序可能和server接收的顺序不一致,表现为车在行进过程中突然掉头。另外,AWheeledVehicleAIController中判断车是否到达某点的距离阈值也要小心设置,若过小,很可能车在上一帧还没有到达该点(大于阈值),下一帧已经超过该点了(大于阈值),就会表现为车在行进过程中突然掉头去往这个已经走过的点。当然,添加位置检测,当车已经超过某点时,即便大于阈值依然pop该轨迹点,我认为会更稳妥些,这样做需注意轨迹点间距和U形弯道的情况。


另外,零零碎碎的提一些小的trick。

  • Carla是左手坐标系。

  • Carla中Server端和Client端的速度单位不统一,km/h和m/s还有cm/s,要注意区分。如PythonAPI中 player.get_velocity()返回值是m/s,而Server中Vehicle.GetVehicleForwardSpeed()是cm/s,SpeedLimit又是km/h。

  • ImportError: No module named agents.navigation.roaming_agent,解决办法:在.bashrc文件中添加 export PYTHONPATH=$PYTHONPATH:CARLA_PATH/PythonAPI/carla

  • 设置server启动时默认加载的地图,方法:在carla/Unreal/CarlaUE4/Config/DefaultEngine.ini中,把TownXX全部改成想要的地图。

[/Script/EngineSettings.GameMapsSettings]
EditorStartupMap=/Game/Carla/Maps/Town04.Town04
GameDefaultMap=/Game/Carla/Maps/Town04.Town04
ServerDefaultMap=/Game/Carla/Maps/Town04.Town04
GlobalDefaultGameMode=/Game/Carla/Blueprints/Game/CarlaGameMode.CarlaGameMode_C
GameInstanceClass=/Script/Carla.CarlaGameInstance
TransitionMap=/Game/Carla/Maps/Town04.Town04
GlobalDefaultServerGameMode=/Game/Carla/Blueprints/Game/CarlaGameMode.CarlaGameMode_C
  • 设置automatic_control.py生成的自车的speed limit,方法:在carla/PythonAPI/carla/agents/navigation/basic_agent.py中,修改初始化函数__init__中的target_speed参数。

class BasicAgent(Agent):
    """
    BasicAgent implements a basic agent that navigates scenes to reach a given
    target destination. This agent respects traffic lights and other vehicles.
    """

    def __init__(self, vehicle, target_speed=120):
        """
        :param vehicle: actor to apply to local planner logic onto
        """
        super(BasicAgent, self).__init__(vehicle)

        self._proximity_threshold = 10.0  # meters
        self._state = AgentState.NAVIGATING
        args_lateral_dict = {
            'K_P': 1,
            'K_D': 0.02,
            'K_I': 0,
            'dt': 1.0/20.0}
        self._local_planner = LocalPlanner(
            self._vehicle, opt_dict={'target_speed' : target_speed,
            'lateral_control_dict':args_lateral_dict})
        self._hop_resolution = 2.0
        self._path_seperation_hop = 2
        self._path_seperation_threshold = 0.5
        self._target_speed = target_speed
        self._grp = None
  • Client类的reload_world()可以destroy原来world中已经创建的所有actors。
client = carla.Client(args.host, args.port)
if client.get_world() is not None:
    client.reload_world();
  • 通过waypoint判断临近的lane是否drivable,可以将waypoint.lane_change与carla.LaneChange做位运算实现。如waypoint.lane_change & carla.LaneChange.Left != 0表示可以左转。参考资料
  • world.get_map().get_spawn_points() 每次返回的都是同样的点集,即推荐的可以生成车辆的地点,这是预先设置好的,共250个点。
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐