CarlaUE4 Linux 编译问题与解决方案

一、MuJoCo 版本不匹配

问题 1.1:大量 MuJoCo API 未声明

错误信息: - use of undeclared identifier 'mjGAIN_DCMOTOR' - use of undeclared identifier 'mjBIAS_DCMOTOR' - use of undeclared identifier 'mjDYN_DCMOTOR' - use of undeclared identifier 'mjNPOLY' - no member named 'sleep_tolerance' in 'mjOption_' - no member named 'body_awake' in 'mjData_' - no member named 'tree_sleep_policy' in 'mjModel_'

根因: CI 流水线安装的 MuJoCo 3.3.5 太旧。上述 API 在 MuJoCo 3.4.0 ~ 3.7.0 之间才逐步引入: - mjNPOLYmjGAIN_DCMOTORmjBIAS_DCMOTORmjDYN_DCMOTOR → 3.7.0 - sleep_tolerancebody_awaketree_sleep_policy → 3.4.0

解决方案: 从 MuJoCo GitHub Releases 下载 3.7.0 Linux 预编译包,替换 third_party/install/MuJoCo/

# 下载
curl -L -o mujoco-3.7.0-linux-x86_64.tar.gz \
  "https://github.com/google-deepmind/mujoco/releases/download/3.7.0/mujoco-3.7.0-linux-x86_64.tar.gz"

# 安装
tar -xzf mujoco-3.7.0-linux-x86_64.tar.gz \
  -C Unreal/CarlaUE4/Plugins/UnrealRoboticsLab/third_party/install/MuJoCo \
  --strip-components=1

# 同时保存到 CI 依赖目录
cp mujoco-3.7.0-linux-x86_64.tar.gz Build/dependencies/Plugins/

涉及文件: - Build/dependencies/Plugins/mujoco-3.7.0-linux-x86_64.tar.gz(新增) - .github/workflows/build_linux.yml(更新版本号)


二、C++ 标准版本

问题 2.1:std::byte 不存在

错误信息:

error: no member named 'byte' in namespace 'std'
error: unknown type name 'mjByteVec'; did you mean 'mjIntVec'?

根因: std::byte 是 C++17 特性。MuJoCo 3.x 的 mjspec.h 使用 std::byte 定义 mjByteVec,但 UE4.26 默认使用 C++14 标准编译。

URLab.Build.cs 中设置 CppStandard = CppStandardVersion.Cpp17 理论上可行,但 UBT 缓存和模块级设置的传播路径不够稳定。

解决方案: 在 MuJoCo 的 mjspec.h 中添加 C++14 兼容层(polyfill):

// 在 #include <vector> 之后、extern "C" { 之前插入:

// C++14 compatibility: std::byte was introduced in C++17.
// UE 4.26 defaults to C++14. Provide a polyfill so MuJoCo compiles.
#if __cplusplus < 201703L
namespace std {
  enum class byte : unsigned char {};
}
#endif

同时在 URLab.Build.cs 中保留 CppStandard = CppStandardVersion.Cpp17 作为兜底。

涉及文件: - third_party/install/MuJoCo/include/mujoco/mjspec.h(polyfill) - Source/URLab/URLab.Build.cs(CppStandard 设置)


三、平台宏污染

问题 3.1:Linux 编译时 __declspec 报错

错误信息:

error: '__declspec' attributes are not enabled;
use '-fdeclspec' or '-fms-extensions' to enable support for __declspec attributes

根因: URLab.Build.cs 中 Linux 分支有 PublicDefinitions.Add("_WIN32=0")。C 预处理器的 #if defined _WIN32 只检查宏是否存在,不看值。#define _WIN32 0defined _WIN32 返回 true,导致 MuJoCo 的 mjexport.h 在 Linux 上走入了 Windows 的 __declspec(dllimport) 分支。

// mjexport.h 的判断逻辑
#if defined _WIN32 || defined __CYGWIN__
  #define MUJOCO_HELPER_DLL_IMPORT __declspec(dllimport)  // ← Linux 上被命中
#else
  #define MUJOCO_HELPER_DLL_IMPORT __attribute__ ((visibility ("default")))
#endif

解决方案:URLab.Build.cs 的 Linux 分支中删除 _WIN32=0USE_DECLSPEC=1

// 修改前
if (Target.Platform == UnrealTargetPlatform.Linux)
{
    PublicDefinitions.Add("_WIN32=0");        // ← 删除
    PublicDefinitions.Add("USE_DECLSPEC=1");   // ← 删除
    PublicDefinitions.Add("__linux__=1");
    PublicDefinitions.Add("__unix__=1");
}

// 修改后
if (Target.Platform == UnrealTargetPlatform.Linux)
{
    CppStandard = CppStandardVersion.Cpp17;
    PublicDefinitions.Add("__linux__=1");
    PublicDefinitions.Add("__unix__=1");
}

涉及文件: - Source/URLab/URLab.Build.cs


问题 3.2:URLab.h 无条件定义 _WIN32

根因: URLab.h 中有如下代码:

#ifndef _WIN32
    #define _WIN32 PLATFORM_WINDOWS  // Linux 上 PLATFORM_WINDOWS = 0
#endif

这导致即使 Build.cs 不再定义 _WIN32,URLab.h 仍然在 Linux 上定义它,同样会触发 MuJoCo 的 __declspec 路径。

解决方案: 改为仅在 Windows 平台定义:

// 修改前
#ifndef _WIN32
    #define _WIN32 PLATFORM_WINDOWS
#endif

// 修改后
// Only define _WIN32 on actual Windows builds, not on Linux
// (PLATFORM_WINDOWS is 0 on Linux, but #define _WIN32 0 still makes
//  `defined _WIN32` true, causing MuJoCo's mjexport.h to use __declspec)
#if PLATFORM_WINDOWS
    #ifndef _WIN32
        #define _WIN32 1
    #endif
#endif

涉及文件: - Source/URLab/Public/URLab.h


问题 3.3:CoACD 第三方库 #if _WIN32 触发 -Wundef

错误信息:

error: '_WIN32' is not defined, evaluates to 0 [-Werror,-Wundef]

根因: 经过上述修复后,Linux 上 _WIN32 不再被定义。但 CoACD 的 coacd.h 中有 #if _WIN32(未定义的宏在 #if 中求值为 0),这在 -Wundef + -Werror 下报错。

解决方案:URLab.Build.cs 中禁用未定义标识符警告:

bEnableUndefinedIdentifierWarnings = false;

涉及文件: - Source/URLab/URLab.Build.cs


四、FbxMeshImportData 枚举冲突

问题 4.1:前向声明与 UE4.26 定义冲突

错误信息:

error: enumeration previously declared with fixed underlying type
note: previous declaration is here

详细:

FbxMeshImportData.h:22:7: error: enumeration previously declared with fixed underlying type
        enum Type
             ^
MujocoGenerationAction.h:34:45: note: previous declaration is here
namespace EFBXNormalGenerationMethod { enum Type : int; }
                                            ^

根因: MujocoGenerationAction.h 中有一个前向声明:

namespace EFBXNormalGenerationMethod { enum Type : int; }

但 UE4.26 的 FbxMeshImportData.h 实际定义是:

namespace EFBXNormalGenerationMethod {
    enum Type {           // ← 没有 : int
        BuiltIn,
        MikkTSpace,
    };
}

C++ 标准不允许前向声明带固定底层类型而定义不带(或反过来)。

解决尝试:

尝试 方案 结果
1 去掉 : intenum Type; C++14 不允许无固定底层类型的枚举前向声明
2 保留 : int → 给定义也加 : int 不能改引擎文件

最终解决方案: 删除前向声明,直接 #include 完整定义的头文件:

// 修改前
// Forward declarations
namespace EFBXNormalGenerationMethod { enum Type : int; }

// 修改后
// Forward declarations
#include "Factories/FbxMeshImportData.h"

涉及文件: - Source/URLabEditor/Public/MujocoGenerationAction.h


五、其他编译错误

问题 5.1:/* 嵌套在块注释中

错误信息:

error: '/*' within block comment [-Werror,-Wcomment]

根因: MjCamera.h:140 的 Doxygen 块注释中包含 tcp://*:5558,其中 //* 被解析为嵌套的 /*,触发了 -Wcomment 警告(-Werror 使其成为错误)。

/** @brief The ZMQ Endpoint for this specific camera (e.g., tcp://*:5558). */
//                                                   ^ 这里的 //* 被看作 /*

解决方案:/** */ 块注释改为 /// 行注释:

/// @brief The ZMQ Endpoint for this specific camera (e.g., tcp://*:5558).

涉及文件: - Source/URLab/Public/MuJoCo/Components/Sensors/MjCamera.h


问题 5.2:EFBXNormalGenerationMethod::MikkTSpace 找不到

错误信息:

error: no member named 'MikkTSpace' in namespace 'EFBXNormalGenerationMethod'
error: no member named 'BuiltIn' in namespace 'EFBXNormalGenerationMethod'

根因: 枚举冲突(见问题 4.1)导致编译器无法正确解析 EFBXNormalGenerationMethod 命名空间中的成员。

解决方案:static_cast 替代命名空间枚举成员访问:

// 修改前
UStaticMesh* ImportedMesh = AttemptMeshImport(ActualSourcePath, DestinationPath,
    EFBXNormalGenerationMethod::MikkTSpace);
ImportedMesh = AttemptMeshImport(ActualSourcePath, DestinationPath,
    EFBXNormalGenerationMethod::BuiltIn);

// 修改后
// UE4.26 workaround: avoid EFBXNormalGenerationMethod::MikkTSpace/BuiltIn
// due to UHT forward-declaration conflict
UStaticMesh* ImportedMesh = AttemptMeshImport(ActualSourcePath, DestinationPath,
    static_cast<EFBXNormalGenerationMethod::Type>(1)); // MikkTSpace
ImportedMesh = AttemptMeshImport(ActualSourcePath, DestinationPath,
    static_cast<EFBXNormalGenerationMethod::Type>(0)); // BuiltIn

涉及文件: - Source/URLabEditor/Private/MujocoMeshImporter.cpp


问题 5.3:FFileHelper 未声明

错误信息:

error: use of undeclared identifier 'FFileHelper'

根因: MujocoMeshImporter.cpp 使用了 FFileHelper::LoadFileToArray 但没有 include 其头文件。

解决方案: 添加 #include "Misc/FileHelper.h"

#include "MujocoGenerationAction.h"
#include "Misc/FileHelper.h"       // ← 新增
#include "URLabEditorLogging.h"

涉及文件: - Source/URLabEditor/Private/MujocoMeshImporter.cpp


问题 5.4:变量未初始化

错误信息:

error: variable 'NumUVChannels' is uninitialized when used here [-Werror,-Wuninitialized]

根因: NumVerticesNumTrianglesNumUVChannels 三个变量声明时未初始化,赋值代码被注释掉。

解决方案: 声明时给初始值 0:

int32 NumVertices = 0;
int32 NumTriangles = 0;
int32 NumUVChannels = 0;

涉及文件: - Source/URLabEditor/Private/MujocoMeshImporter.cpp


六、第三方库

问题 6.1:CoACD 头文件找不到

错误信息:

fatal error: 'Coacd/coacd.h' file not found

根因: CoACD.zip 解压后的目录结构为 CoACD/CoACD/include/CoACD/coacd.h(嵌套了一层),而 URLab.Build.csAddThirdPartyLibrary 函数添加的 include 路径是 CoACD/include/,导致 coacd.h 不在搜索路径中。

解决方案:

  1. 将嵌套的 include 目录移出来:

    mv CoACD/CoACD/include CoACD/include
    

  2. 目录名 CoACD(大写)和代码中的 #include "Coacd/coacd.h"(小写 d)不匹配。Linux 文件系统区分大小写,需要统一:

    mv CoACD/include/CoACD CoACD/include/Coacd
    

  3. CI 工作流同步修复:

    # .github/workflows/build_linux.yml
    unzip -qo Build/dependencies/Plugins/CoACD.zip \
      -d Unreal/CarlaUE4/Plugins/UnrealRoboticsLab/third_party/install/CoACD
    
    # 新增:修复目录嵌套
    if [ -d .../CoACD/CoACD/include ] && [ ! -d .../CoACD/include ]; then
      mv .../CoACD/CoACD/include .../CoACD/include
      mv .../CoACD/include/CoACD .../CoACD/include/Coacd
    fi
    

涉及文件: - third_party/install/CoACD/include/(目录结构调整) - .github/workflows/build_linux.yml(CI 同步)


问题 6.2:ZMQ 头文件找不到

错误信息:

fatal error: 'zmq.h' file not found

根因: CI 提供的 libzmq-linux.tar.gz 预编译包只包含 .so 动态库,include/ 目录为空,没有 zmq.h

解决方案: 系统已安装 libzmq3-dev/usr/include/zmq.h),创建软链接:

ln -sf /usr/include/zmq.h \
  third_party/install/libzmq/include/zmq.h

涉及文件: - third_party/install/libzmq/include/zmq.h(软链接)


修改文件清单

# 文件 改动类型 解决问题
1 third_party/install/MuJoCo/ 替换为 3.7.0 1.1 API 缺失
2 mjspec.h 添加 C++14 polyfill 2.1 std::byte
3 URLab.Build.cs _WIN32=0、加 Cpp17、禁用 -Wundef 3.1 __declspec、2.1、3.3
4 URLab.h _WIN32 仅 Windows 定义 3.2 宏污染
5 MujocoGenerationAction.h 前向声明 → include 头文件 4.1 枚举冲突
6 MujocoMeshImporter.cpp 枚举 cast + include + 初始化 5.2、5.3、5.4
7 MjCamera.h /**/// 5.1 嵌套注释
8 CoACD/include/ 目录结构调整 6.1 CoACD
9 libzmq/include/ 软链接 zmq.h 6.2 ZMQ
10 build_linux.yml MuJoCo 版本 + CoACD 修复 CI 同步

编译前必须执行的步骤

每次修改 Build.cs、头文件或第三方库后,UBT 缓存可能导致旧配置被使用。编译前务必清理缓存:

cd /path/to/hutb

# 清理 UBT 缓存
rm -rf /home/ubuntu/UnrealEngine_4.26/Engine/Intermediate/Build/Linux/B4D820EA

# 清理项目中间文件
rm -rf Unreal/CarlaUE4/Intermediate

# 编译
source ./setEnv64.sh
make CarlaUE4Editor ARGS="--chrono"