3. 第一台自动驾驶车

好,我们正式开始动手了。

上一章我们把CARLA环境搭好,也跑通了那个小栗子。说实话,看到那辆蓝色小车在屏幕上动起来的时候,我自己当年第一次跑通也是挺激动的。但那个demo太简单了,说白了就是让车自己瞎跑。

这一章,我们要亲手造出第一台属于我们的自动驾驶车。我会用特斯拉Model3作为原型,带你把油门、刹车、转向都控制起来,再实时读取它的速度、位置、加速度。最后,我们还要调一调它的物理属性,让它开起来更真实。

3.1 生成一辆特斯拉Model3

CARLA自带的车辆模型里,刚好有特斯拉Model3。我个人习惯是每次启动仿真前,先清空一下世界里的老演员,免得上一轮的车还停在路上。

import carla

# 连接客户端
client = carla.Client('localhost', 2000)
client.set_timeout(10.0)

# 获取世界对象
world = client.get_world()

# 清空所有老演员(车辆、行人等)
for actor in world.get_actors().filter('vehicle.*'):
    actor.destroy()

嗯,这里要注意:destroy() 操作会直接删除演员,如果你后面还要用它的数据,记得先存一下。

接下来,我们生成一辆特斯拉Model3:

# 获取蓝图库
blueprint_library = world.get_blueprint_library()

# 筛选出特斯拉Model3的蓝图
tesla_bp = blueprint_library.find('vehicle.tesla.model3')

# 设置生成点(我习惯用地图上的随机生成点)
spawn_points = world.get_map().get_spawn_points()
spawn_point = spawn_points[0]  # 选第一个生成点

# 生成车辆
vehicle = world.spawn_actor(tesla_bp, spawn_point)

print(f"特斯拉已生成,ID: {vehicle.id}")

为什么选第一个生成点?其实无所谓,你完全可以随机选一个。我在项目里遇到过一个问题:有些生成点被其他车辆占了,导致 spawn_actor 报错。所以稳妥的做法是加个 try-except,或者先检查一下这个点有没有障碍物。

小技巧: 如果你想要车辆出现在某个特定位置(比如十字路口正中央),可以自己构造一个 carla.Transform,手动指定 x、y、z 坐标和朝向。

3.2 控制车辆运动:油门、刹车、转向

车生出来了,怎么让它动?CARLA 里控制车辆运动的核心是 VehicleControl 这个类。它包含几个关键字段:

字段 类型 说明
throttle float (0.0 ~ 1.0) 油门,0 是松油,1 是地板油
brake float (0.0 ~ 1.0) 刹车,0 不刹,1 刹死
steer float (-1.0 ~ 1.0) 转向,-1 左满舵,1 右满舵
hand_brake bool 手刹,一般不用
reverse bool 倒车档

举个例子,让车直线加速:

# 创建一个控制指令
control = carla.VehicleControl()
control.throttle = 0.5   # 50% 油门
control.brake = 0.0      # 不刹车
control.steer = 0.0      # 方向盘回正

# 应用控制
vehicle.apply_control(control)

你想想看,如果只给一次控制,车只会动一帧然后就停了。所以我们需要在循环里持续发送控制指令。我一般用 while True 配合 time.sleep 来控制频率:

import time

# 持续加速 3 秒
for i in range(30):  # 假设每 0.1 秒发一次,共 3 秒
    control.throttle = 0.6
    vehicle.apply_control(control)
    time.sleep(0.1)

# 然后刹车
control.throttle = 0.0
control.brake = 0.8
vehicle.apply_control(control)
time.sleep(1)
注意: 油门和刹车不要同时给非零值。CARLA 内部会优先处理刹车,但逻辑上这属于错误操作。我曾经在调试时手滑写了个 throttle=0.5 又 brake=0.3,结果车又慢又抖,排查了半天才发现。

3.3 获取车辆状态信息

光让车跑还不够,我们得知道它跑得怎么样。CARLA 提供了 VehiclePhysicsControl 和 VehicleControl 的实时数据,但最常用的是通过 get_velocity()、get_location()、get_acceleration() 这三个方法。

来,我们写个函数,把状态打印出来:

def print_vehicle_state(vehicle):
    # 速度(返回的是三维向量,单位 m/s)
    velocity = vehicle.get_velocity()
    speed = (velocity.x**2 + velocity.y**2 + velocity.z**2) ** 0.5
    speed_kmh = speed * 3.6  # 转成 km/h

    # 位置(返回 carla.Location)
    location = vehicle.get_location()

    # 加速度(返回三维向量)
    acceleration = vehicle.get_acceleration()
    acc_magnitude = (acceleration.x**2 + acceleration.y**2 + acceleration.z**2) ** 0.5

    print(f"速度: {speed_kmh:.2f} km/h")
    print(f"位置: ({location.x:.2f}, {location.y:.2f}, {location.z:.2f})")
    print(f"加速度: {acc_magnitude:.2f} m/s²")

把这个函数放到控制循环里,每帧调用一次,你就能实时看到车辆状态了。我个人习惯把速度转成 km/h,因为平时看仪表盘习惯了,m/s 总觉得不直观。

核心知识点: 速度是矢量,get_velocity() 返回的是三维向量。如果你只关心前进方向的速度,可以投影到车辆的前方向量上。这在做纵向控制时非常有用。

3.4 设置车辆物理属性

默认的车辆物理参数是 CARLA 根据真实车型粗略设定的。但有时候我们需要调一调,比如让车更飘一点(模拟雨天),或者更稳一点(模拟满载)。

物理属性通过 VehiclePhysicsControl 来设置。常用的参数有:

参数 说明 典型值
mass 车辆质量(kg) 1800(Model3 约 1800kg)
drag_coefficient 空气阻力系数 0.23(Model3 很低)
max_steering_angle 最大转向角(度) 40
tire_friction 轮胎摩擦系数 1.0(干地),0.5(湿地)

举个例子,把车改成「雨天模式」:

# 获取当前物理控制
physics_control = vehicle.get_physics_control()

# 修改参数
physics_control.mass = 1900  # 稍微重点,模拟积水
physics_control.tire_friction = 0.6  # 降低摩擦,模拟湿滑路面
physics_control.drag_coefficient = 0.3  # 风阻大一点

# 应用修改
vehicle.apply_physics_control(physics_control)

print("物理属性已更新:雨天模式")

嗯,这里有个坑:apply_physics_control 之后,车辆不会立刻改变状态,它需要等下一个物理步长才会生效。我建议你在设置完后,等个 0.1 秒再开始控制,否则第一帧的数据可能还是旧的。

避坑指南: 我曾经在调试时把 tire_friction 设成了 0.1,结果车一给油门就在原地打转,根本走不动。后来才发现是摩擦系数太小了。建议你每次改完参数,先小油门测试一下,别一上来就地板油。

3.5 本章知识体系

下面这张图帮你梳理了本章的核心逻辑:从生成车辆,到控制运动,再到获取状态和调物理属性,环环相扣。

第一台自动驾驶车 - 知识体系 1. 生成特斯拉Model3 spawn_actor + 蓝图 2. 控制车辆运动 油门/刹车/转向 3. 获取状态 速度/位置/加速度 4. 设置物理属性 质量/摩擦/风阻/转向角 核心流程:生成 → 控制 → 感知状态 → 调参优化 关键参数速查 throttle: 0~1 brake: 0~1 steer: -1~1 mass: 1800kg tire_friction: 0.5~1.0 drag: 0.23~0.3

3.6 完整示例代码

最后,我把上面所有步骤整合成一个完整的脚本。你直接复制到 CARLA 的 Python 环境下运行就行:

import carla
import time

def main():
    # 连接
    client = carla.Client('localhost', 2000)
    client.set_timeout(10.0)
    world = client.get_world()

    # 清空旧车
    for actor in world.get_actors().filter('vehicle.*'):
        actor.destroy()

    # 生成特斯拉
    blueprint_library = world.get_blueprint_library()
    tesla_bp = blueprint_library.find('vehicle.tesla.model3')
    spawn_points = world.get_map().get_spawn_points()
    spawn_point = spawn_points[0]
    vehicle = world.spawn_actor(tesla_bp, spawn_point)
    print("特斯拉已生成")

    # 设置物理属性(雨天模式)
    physics_control = vehicle.get_physics_control()
    physics_control.tire_friction = 0.6
    physics_control.mass = 1900
    vehicle.apply_physics_control(physics_control)
    time.sleep(0.1)

    # 控制循环
    control = carla.VehicleControl()
    for i in range(50):  # 约5秒
        # 前3秒加速,后2秒刹车
        if i < 30:
            control.throttle = 0.5
            control.brake = 0.0
        else:
            control.throttle = 0.0
            control.brake = 0.7

        control.steer = 0.0
        vehicle.apply_control(control)

        # 获取状态
        velocity = vehicle.get_velocity()
        speed = (velocity.x**2 + velocity.y**2 + velocity.z**2) ** 0.5 * 3.6
        location = vehicle.get_location()
        acc = vehicle.get_acceleration()
        acc_mag = (acc.x**2 + acc.y**2 + acc.z**2) ** 0.5

        print(f"帧{i:2d} | 速度:{speed:5.1f} km/h | 位置:({location.x:6.1f},{location.y:6.1f}) | 加速度:{acc_mag:4.2f} m/s²")

        time.sleep(0.1)

    # 清理
    vehicle.destroy()
    print("测试完成,车辆已销毁")

if __name__ == '__main__':
    main()

运行这个脚本,你会看到特斯拉从静止加速到大概 50 km/h,然后刹车停下。同时终端会实时打印每一帧的状态数据。

嗯,到这里,你已经亲手造出了第一台自动驾驶车。虽然它现在只会直行和刹车,但你已经掌握了最核心的三个能力:生成、控制、感知。下一章我们会让这辆车学会转弯和跟车,敬请期待。

本章总结:
  • 用 spawn_actor 生成特斯拉Model3
  • 用 VehicleControl 控制油门、刹车、转向
  • 用 get_velocity()、get_location()、get_acceleration() 获取状态
  • 用 VehiclePhysicsControl 调整物理属性