| name | unreal-engine |
| description | Unreal Engine 机器人仿真开发技能 - 高保真仿真、物理引擎集成、传感器模拟 |
| argument-hint | unreal仿真 OR UE机器人 OR 创建仿真场景 OR 虚幻引擎 |
| user-invocable | true |
Unreal Engine Robot Simulation Skill
用于 Unreal Engine 机器人仿真环境的配置和开发
何时使用
当需要以下帮助时使用此技能:
- 使用 Unreal Engine 创建机器人仿真
- 配置高保真传感器模拟
- 集成 ROS2/ROS1
- 开发自定义插件
- 构建虚拟环境
快速参考
系统要求
- 操作系统: Windows 10/11 或 Linux (通过 Unreal Engine 5.4+)
- GPU: NVIDIA RTX 2080+ (推荐 RTX 3080+)
- 显存: 8GB+ (强烈建议 16GB+)
- 存储: 100GB+ SSD
- 内存: 16GB+
安装 Unreal Engine
git clone -b 5.4 https://github.com/EpicGames/UnrealEngine.git
cd UnrealEngine
./Setup.sh
./GenerateProjectFiles.sh
创建机器人项目
目录结构
RobotSimulation/
├── Content/ # 资源
│ ├── Robots/ # 机器人模型
│ ├── Environments/ # 环境
│ ├── Sensors/ # 传感器
│ └── Maps/ # 地图
├── Source/ # 源代码
│ ├── RobotSimulation/ # 主模块
│ ├── Ros2Bridge/ # ROS 桥接
│ └── Sensors/ # 传感器插件
├── Config/ # 配置文件
└── Saved/ # 保存数据
ROS 集成
安装 Unreal Engine ROS2 插件
cd ~/UnrealProjects/RobotSimulation/Plugins
git clone https://github.com/roboticsbuildingblocks/unreal_ros2_bridge.git
cd ~/UnrealProjects/RobotSimulation
./Build.sh
ROS2 桥接配置
PublicDependencyModuleNames.AddRange(
new string[]
{
"Core",
"CoreUObject",
"Engine",
"InputCore",
"rosidl_typesupport_cpp",
"rclcpp",
}
);
发布话题
#include "Ros2Bridge/Public/ROS2Publisher.h"
void AMyRobotActor::BeginPlay()
{
Super::BeginPlay();
Publisher = NewObject<UROS2Publisher>(this);
Publisher->Initialize(
"/robot/odometry",
"nav_msgs/msg/Odometry",
this);
}
void AMyRobotActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
FROS2OdometryMsg Msg;
Msg.header.stamp = FROSTime::Now();
Msg.header.frame_id = "odom";
Msg.child_frame_id = "base_link";
Msg.pose.pose.position.X = GetActorLocation().X / 100.0;
Msg.pose.pose.position.Y = GetActorLocation().Y / 100.0;
Msg.twist.twist.linear.x = Velocity.X;
Publisher->Publish(Msg);
}
订阅话题
Subscriber = NewObject<UROS2Subscriber>(this);
Subscriber->Initialize(
"/cmd_vel",
"geometry_msgs/msg/Twist",
this,
&AMyRobotActor::OnCmdVelReceived);
void AMyRobotActor::OnCmdVelReceived(UROS2Msg* Msg)
{
UGeometryMsgTwist* TwistMsg = Cast<UGeometryMsgTwist>(Msg);
TargetVelocity.X = TwistMsg->linear.x;
TargetVelocity.Y = TwistMsg->linear.y;
TargetVelocity.Z = TwistMsg->angular.z;
}
机器人模型
导入机器人模型
创建骨骼网格体
UCLASS()
class AMyRobot : public AActor
{
GENERATED_BODY()
public:
UPROPERTY(VisibleAnywhere)
USkeletalMeshComponent* SkeletalMesh;
UPROPERTY(VisibleAnywhere)
UPhysicsConstraintComponent* LeftWheelJoint;
UPROPERTY(VisibleAnywhere)
UPhysicsConstraintComponent* RightWheelJoint;
};
void AMyRobot::BeginPlay()
{
Super::BeginPlay();
LeftWheelJoint->SetConstrainedComponents(
SkeletalMesh, "wheel_bone_l",
MeshRoot, "base_bone"
);
LeftWheelJoint->SetAngularSwing1Limit(
ACM_Locked, 0.0
);
LeftWheelJoint->SetAngularDriveMode(
EAngularDriveMode::Velocity
);
LeftWheelJoint->SetAngularVelocityDrive(
true, true
);
}
物理配置
UPhysicsAsset* PhysicsAsset = Mesh->GetPhysicsAsset();
for (UPhysicsBodySetup* Body : PhysicsAsset->PhysicsBodySetup)
{
Body->CollisionEnabled = ECollisionEnabled::QueryAndPhysics;
Body->Mass = 5.0f;
Body->InertiaTensor = FVector(0.1f, 0.1f, 0.1f);
}
传感器模拟
RGB 摄像头
UCameraComponent* Camera = NewObject<UCameraComponent>(this);
Camera->SetRelativeLocation(FVector(10.0f, 0.0f, 5.0f));
Camera->SetFieldOfView(90.0f);
Camera->bUseFieldOfViewForLOD = true;
Camera->RegisterComponent();
void AMyRobot::Tick(float DeltaTime)
{
if (Camera->RenderTarget)
{
FTextureResource* Texture = Camera->RenderTarget->GetResource();
FUInt8Image Image;
Camera->RenderTarget->ReadPixels(Image);
ImagePublisher->Publish(Image);
}
}
深度摄像头
DepthCamera->CameraSettings->DepthOfFieldMethod = EDepthOfFieldMethod::SDF;
DepthCamera->CameraSettings->bOverride_DepthOfFieldBlurRadius = true;
DepthCamera->CameraSettings->DepthOfFieldBlurAmount = 0.0f;
DepthCamera->CameraSettings->bWriteDepth = true;
void AMyRobot::GetDepthImage(TArray<float>& DepthData)
{
FRenderTarget* Target = DepthCamera->RenderTarget.Get();
TArray<FColor> Colors;
Target->ReadPixels(Colors);
for (int32 i = 0; i < Colors.Num(); i++)
{
DepthData.Add(Colors[i].R / 255.0f * MaxDepth);
}
}
激光雷达
UCLASS()
class ALidarSensor : public UActorComponent
{
UPROPERTY(EditAnywhere)
int32 ScanResolution = 360;
UPROPERTY(EditAnywhere)
float MaxRange = 100.0f;
UPROPERTY(EditAnywhere)
float MinRange = 0.1f;
UPROPERTY(EditAnywhere)
float HorizontalFOV = 360.0f;
UPROPERTY(EditAnywhere)
float VerticalFOV = 30.0f;
UPROPERTY(EditAnywhere)
int32 VerticalResolution = 16;
};
void ALidarSensor::TickComponent()
{
TArray<FHitResult> Hits;
FCollisionQueryParams QueryParams;
QueryParams.AddIgnoredActor(GetOwner());
for (int32 v = 0; v < VerticalResolution; v++)
{
for (int32 h = 0; h < ScanResolution; h++)
{
float AngleH = (h * HorizontalFOV / ScanResolution) - (HorizontalFOV / 2);
float AngleV = (v * VerticalFOV / VerticalResolution) - (VerticalFOV / 2);
FVector Direction = UKismetMathLibrary::Conv_EulerToVector(AngleV, AngleH, 0);
FVector End = GetComponentLocation() + Direction * MaxRange;
bool bHit = GetWorld()->LineTraceSingleByChannel(
Hits,
(),
End,
ECC_Visibility,
QueryParams
);
(bHit)
{
PointCloud.(Hits[].Distance);
}
{
PointCloud.(MaxRange);
}
}
}
LidarPublisher->(PointCloud);
}
IMU 传感器
UCLASS()
class AImuSensor : public UActorComponent
{
UPROPERTY(EditAnywhere)
float AccelerationNoise = 0.01f;
UPROPERTY(EditAnywhere)
float GyroNoise = 0.001f;
UPROPERTY(EditAnywhere)
float UpdateRate = 100.0f;
};
void AImuSensor::TickComponent()
{
AActor* Parent = GetOwner();
FVector Velocity = Parent->GetVelocity();
FVector AngularVelocity = Parent->GetAngularVelocityInDegrees();
FVector AccelNoise(
FMath::RandRange(-AccelerationNoise, AccelerationNoise),
FMath::RandRange(-AccelerationNoise, AccelerationNoise),
FMath::RandRange(-AccelerationNoise, AccelerationNoise)
);
FVector LinearAcceleration = -Velocity.GetSafeNormal() * 9.81;
FROS2ImuMsg ImuMsg;
ImuMsg.header.stamp = FROSTime::Now();
ImuMsg.angular_velocity.x = AngularVelocity.X * PI / 180.0;
ImuMsg.angular_velocity.y = AngularVelocity.Y * PI / 180.0;
ImuMsg.angular_velocity.z = AngularVelocity.Z * PI / 180.0;
ImuMsg.linear_acceleration = LinearAcceleration + AccelNoise;
Publisher->Publish(ImuMsg);
}
环境构建
创建地形
添加静态物体
void ASpawner::SpawnObjects()
{
for (int32 i = 0; i < 100; i++)
{
FVector Location(
FMath::RandRange(-500, 500),
FMath::RandRange(-500, 500),
0
);
FActorSpawnParameters Params;
Params.SpawnCollisionHandlingOverride =
ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn;
GetWorld()->SpawnActor<AActor>(
CubeClass,
Location,
FRotator::ZeroRotator,
Params
);
}
}
光照设置
UDirectionalLightComponent* Sun = NewObject<UDirectionalLightComponent>(this);
Sun->SetIntensity(3.0f);
Sun->SetLightColor(FLinearColor(1.0f, 0.95f, 0.9f));
Sun->SetActorRotation(FRotator(-45, 45, 0));
Sun->RegisterComponent();
Sun->SetCastShadows(true);
Sun->SetShadowBias(0.5f);
Sun->SetShadowSlopeBias(0.5f);
性能优化
多线程
void AMyRobot::Tick(float DeltaTime)
{
FVector InputForce = CalculateInputForce();
ENQUEUE_UNREAL_FUNCTION(
Mesh->AddForce(InputForce * 1000.0f);
);
}
LOD 设置
for (USkeletalMeshLODInfo* LODInfo : SkeletalMesh->LODInfo)
{
LODInfo->ScreenSize = 0.5f;
}
Texture->LODGroup = TEXTUREGROUP_World;
Texture->MipLoadSettings = EMipLoadSettings::MipLoadOnStart;
渲染优化
PostProcess->bAllowMotionBlur = false;
PostProcess->bAllowTemporalAA = false;
Sun->SetShadowDistanceFadeoutMultiplier(0.1f);
打包与部署
Windows 打包
UnrealPak.exe -create=Project.upack -output=Ship/WindowsNoEditor.pak
Linux 部署
./Build.sh Linux Development -SkipBuild -SkipDeploy
docker build -t unreal_robot_sim .
常见问题
问题 1: 性能不足
解决方案:
- 降低渲染分辨率
- 减少并行环境数
- 禁用不必要的视觉效果
问题 2: ROS 连接失败
解决方案:
- 检查网络设置
- 确认 ROS_DOMAIN_ID
- 验证话题名称匹配
问题 3: 物理不稳定
解决方案:
相关资源
另见