CAN and CARLA integration

Drive a CARLA ego vehicle from Safety Island control output over classic CAN. CARLA has no native CAN interface, so a host bridge stands in for the vehicle ECU: SocketCAN RX, decode the placeholder frames from CAN output, apply carla.VehicleAckermannControl.

This page is the design for the stacked work tracked in https://github.com/autowarefoundation/autoware-safety-island/issues/43. Implementation details, backend isolation, the Zephyr FVP UDP tunnel, and the decoder/watchdog rules live in CAN and CARLA implementation plan.

graph LR POSIX[freertos-posix] VCAN[SocketCAN vcan0] Bridge[Host CAN-CARLA bridge] CARLA[CARLA ego] POSIX -->|classic CAN 0x100 0x101 0x102| VCAN VCAN --> Bridge Bridge -->|VehicleAckermannControl| CARLA

Shared decisions

  • Actuation path is CAN. The bridge talks to CARLA through the Python API, not by republishing ROS control commands. That keeps CAN first-class: CARLA is the simulated vehicle, not another ROS hop.

  • DDS inputs stay DDS. Trajectory, odometry, steering, acceleration, and operation mode are unchanged.

  • Placeholder frame contract. Keep IDs 0x100 / 0x101 / 0x102 and the scaling in CAN output until a vehicle DBC is agreed.

  • One host bridge, one sink. Open-loop and closed-loop share the same SocketCAN → decode → CARLA Python API process. Closed-loop does not add a second command path through autoware_carla_interface.

  • PR 1 local runtime is freertos-posix plus vcan.

  • PR 3 local runtime is zephyr-fvp --network tap plus a UDP batch tunnel onto the same vcan0 decoder. Native FVP CAN stays loopback-only. S32Z hardware CAN is out of this stack.

  • CI never launches CARLA. GitHub Actions covers encode → transport → decode. The visual CARLA loop is a documented host demo.

  • Mock is test-only. Runtime CAN output does not fall back to the in-memory recorder when no interface is configured.

  • CAN-FD is opt-in. freertos-posix can send one 24-byte 0x103 frame instead of the classic three (SAFETY_ISLAND_CAN_FORMAT=fd plus bridge.py --can-format fd). Every other runtime stays classic; see CAN-FD.

Open-loop (PR 1)

Goal: a Safety Island command visibly moves a CARLA ego without Autoware planning or Open AD Kit.

Components

  1. SocketCAN backend for freertos-posix. Isolated with PLATFORM_FREERTOS_POSIX so Linux PF_CAN never enters S32Z2/X5H builds. SAFETY_ISLAND_CAN_IFACE (for example vcan0) is required at runtime; missing or unopenable interface fails can_init(). The in-memory mock is compiled only into --can-output-test.

  2. Python host decoder in demo/can_carla_bridge/. 0x102 is the commit marker: require fresh 0x100 and 0x101, wrapping sequence (forward jumps accepted, replays/stale rejected), and a 0.5 s monotonic receive-timeout safe stop. Python state-machine tests and a privileged vcan integration phase validate the decoder against the C++ sender.

  3. Host bridge (demo/can_carla_bridge/): python-can on vcan0, assemble a command from the three frames, map to VehicleAckermannControl, apply via the CARLA 0.9.16 client. Same CARLA version as Open AD Kit.

  4. Inputs to the controller from the existing rosbag (or a canned DDS publisher) on domain 2. Do not start planning simulator or Open AD Kit in this loop.

Control mapping

autoware_control_msgs/Control is kinematic and left-positive. CARLA Ackermann control is right-positive. The bridge uses carla.VehicleAckermannControl. Sign conversion is steer = -steering_tire_angle only: CARLA takes Abs(steer_speed). Zero steer_speed snaps steer; zero acceleration means vehicle max, not “hold”. Safe stop is speed = 0 with configured brake and last steer, not all zeros. Details are in CAN and CARLA implementation plan.

Demo and validation

  • Host: ip link add vcan0 type vcan and bring it up.

  • Build freertos-posix with --control-output CAN_ONLY or DDS_AND_CAN, SAFETY_ISLAND_CAN_IFACE=vcan0.

  • Run the bridge against CARLA 0.9.16, confirm the ego steers/throttles.

  • CI: privilege-free encoder/decoder/mock tests, plus a capability-gated vcan integration job. No GPU, no CARLA container.

Known limits

  • Placeholder CAN IDs, not a production DBC.

  • Open-loop CARLA motion will not match rosbag odometry.

  • Mapping cannot recover jerk from the current frames.

  • FVP TAP UDP is PR 3, not this demo.

  • S32Z hardware CAN is not this demo.

Closed-loop with Open AD Kit is PR 2. Zephyr FVP is PR 3. CAN-FD is opt-in on the POSIX path; see CAN-FD.

Closed-loop (PR 2)

Goal: Autoware planning against CARLA, Safety Island as the controller, actuation still over CAN into the same host bridge.

This layer depends on the open-loop SocketCAN backend and bridge. It does not add a second command sink.

graph LR subgraph carla_stack [Open AD Kit CARLA] Sensors[autoware_carla_interface<br/>sensors only] Planning[Autoware planning] CARLA2[CARLA ego] end Bridge2[Host CAN-CARLA bridge] DDS[domain bridge] SI2[Safety Island<br/>CAN_ONLY] CARLA2 --> Sensors Sensors --> Planning Planning -->|trajectory odom steer accel| DDS DDS --> SI2 SI2 -->|classic CAN| Bridge2 Bridge2 -->|VehicleAckermannControl| CARLA2

Rules

  • Safety Island builds with CAN_ONLY so /control/trajectory_follower/control_cmd is not also published over DDS.

  • Autoware’s onboard trajectory follower stays running so Auto/Engage can set AUTONOMOUS. It must not actuate CARLA: sensors-only autoware_carla_interface skips apply_control() and remaps command topics off the live path.

  • autoware_carla_interface keeps sensor and localization publication and the CARLA world tick. It has no upstream sensor-only flag: SensorLoop calls ego.apply_control() every tick. The overlay Open AD Kit’s overlay/patch_sensors_only.py skips that call. Remap the raw-vehicle converter off the live command topics. Do not fork Open AD Kit or Autoware Universe.

  • Same placeholder frames, same mapping, same vcan0 iface.

  • Autoware plus CARLA compose lives in Open AD Kit deployments/safety-island-carla-simulation/ (PR #146). That deployment also starts the domain-bridge. This repository runs SI POSIX CAN_ONLY, vcan0, and the CAN-CARLA bridge. Do not vendor Open AD Kit sources here.

Validation

  • Manual: start Open AD Kit safety-island-carla-simulation (no --drive), then this repo’s SI CAN_ONLY and CAN-CARLA bridge. Town01, set a goal, engage, confirm the ego moves under SI CAN commands and that candump vcan0 shows 0x100 / 0x101 / 0x102.

  • Confirm Autoware is not also applying control_cmd to CARLA (no dual drive): apply_control is patched out and the converter is remapped.

  • Still no CARLA job in this repository’s GitHub Actions.

Known limits

  • GPU host and Open AD Kit image pull required.

  • Humble-only while Open AD Kit CARLA is Humble-only.

  • Placeholder DBC and simple actuator mapping still apply.

  • CES / FVP virtual-CAN is PR 3 below.

Zephyr FVP (PR 3)

Goal: the same open-loop and closed-loop CARLA demos as PR 1 and PR 2, with Safety Island on zephyr-fvp --network tap instead of freertos-posix. Frames still terminate on vcan0. The host decoder, watchdog, and CARLA bridge do not change.

Native FVP CAN stays zephyr,can-loopback (unit tests only). This PR is a UDP batch tunnel over the existing TAP Ethernet path.

graph LR FVP[zephyr-fvp TAP] TAP[LAN91C111 tap0] GW[UDP-to-CAN gateway] VCAN[SocketCAN vcan0] Bridge[Host CAN-CARLA bridge] CARLA[CARLA ego] FVP -->|48-byte UDP batch| TAP TAP --> GW GW -->|0x100 0x101 0x102| VCAN VCAN --> Bridge Bridge -->|VehicleAckermannControl| CARLA

Open-loop

Same as PR 1, except the producer:

  • Build zephyr-fvp with --network tap --control-output CAN_ONLY (or DDS_AND_CAN).

  • FVP 192.168.10.2 sends one UDP datagram per command to 192.168.10.1:5555.

  • demo/can_tunnel_bridge/ validates the datagram and injects classic frames onto vcan0.

  • Rosbag or canned DDS inputs on domain 2. No Open AD Kit.

Closed-loop

Same Open AD Kit rules as PR 2 (CAN_ONLY, sensors-only autoware_carla_interface, no dual-drive). Safety Island is zephyr-fvp TAP plus the gateway, not freertos-posix.

Kconfig

CONTROL_CMD_OUTPUT_CAN_ONLY / DDS_AND_CAN must not select CAN. Only CONFIG_CONTROL_CMD_CAN_TRANSPORT_NATIVE selects CAN. CONFIG_CONTROL_CMD_CAN_TRANSPORT_UDP_TUNNEL selects NET_UDP / NET_IPV4 and does not require zephyr,canbus. platform_can.h includes zephyr_can_udp_tunnel.h on the TAP path so zephyr_can.h is not compiled.

Watchdog

POSIX stays at 0.5 s. FVP is not real-time; the FVP demo and TAP integration job use a much larger configured timeout.

Validation

  • Privilege-free: UDP pack/unpack unit tests; native loopback --can-output-test unchanged.

  • Privileged: FVP TAP + gateway → vcan0 → same decoder as PR 1. No CARLA in CI.

  • Manual: PR 1 and PR 2 host demos with zephyr-fvp as the producer.

Known limits

  • CAN-over-UDP, not classic CAN end-to-end.

  • S32Z hardware CAN remains out of this stack.

  • CAN-FD is opt-in and POSIX-only; the FVP tunnel stays classic.