文章目录
前言
为什么要使用VS Connect:
联合仿真:VS Connect API 允许在多个仿真软件之间进行同步仿真。这意味着可以在同一时间步内,多个仿真程序共享数据和状态信息,从而实现更复杂的联合仿真场景。例如,CarSim 可以与 TruckSim 或 BikeSim 同步运行,以模拟不同类型车辆在同一交通环境中的相互影响。
分布式仿真:VS Connect API 支持分布式仿真,这意味着仿真程序可以运行在不同的计算机上,通过网络( UDP)进行通信和数据交换。这对于需要大量计算资源的仿真场景特别有用,因为可以利用多台计算机的计算能力来分担仿真负载。
实时通信:通过 UDP 协议,VS Connect API 能够实现低延迟的实时数据通信。这对于需要实时交互和快速响应的仿真应用非常重要,例如实时驾驶模拟器、交通仿真系统等。
关键: carsim求解器与VS Connect方法不同的是,carsim求解器调用方法无法在运行的时候加入时间延迟以控制求解器的运行频率,一旦加入时间延迟将无法正常输出动力学信息。
正常情况下,carsim求解器的运行周期大概为0.0005s,显然我们正常的显卡是无法让仿真软件画面的更新频率达到0.0005s,当然你也可以让carsim以0.0005s的运行频率计算动力学信息,然后仿真软件以0.01s或者其他时间去获取动力学信息并更新仿真动画。Vs Connect已经兼容这种方式,并且支持多个通讯服务。
CARLA官方集成CARSIM的方法,其实就是CARSIM给UE专门开发了一个VehicleSim Dynamics插件,原理就是通过Vs Connect实现;
一、VS Connect 概念引入
名称 | 描述 |
---|---|
VscNode | 表示本地通信端点,并包含多个 VscLink。应用程序可以创建任意数量的 VscNode。 |
VscLink | 表示与远程 VS Connect 节点的通信通道。要建立连接,一个 VscLink 需要与本地的 VscNode 对象关联。一个 VscNode 可以关联多个 VscLink。 |
VscField | 通俗的讲就是要用于存放 carsim 变量参数的结构体,如变量名为“IMP_PCON_BK”,属性为“ADD 0.0”。 |
VscSchema | VscField 的有序集合。schema 描述了发送或接收的数据更新的结构。一个 schema 可以关联多个 VscField。 |
VscUpdateDefinition | 描述将发送哪些数据以及何时发送。VscUpdateDefinition 包括一个 VscSchema(将发送什么)和调度参数(何时发送数据)。 |
VscContract | 两个连接节点之间的协议,描述了要发送的数据以及发送时间。VscContract 将 VscUpdateDefinition 与 VscLink 关联。Contract 是节点之间传输数据的完整描述,包括传输方向、数据结构(Schema)和数据发送的时间表。 |
VscUpdateData | 在单一更新时间(仿真时间)内的一包仿真数据。VS Connect 的主要目的是允许应用程序交换 VscUpdateData 实例中包含的数据。VscUpdateData 的实例与 VscContract 及其关联的 VscSchema 相关联,后者描述了更新中包含的数据的结构。 |
二、VS Connect 通讯框架
三、Carsim 工程配置
1、车辆模型配置
随便选择个车辆模型
2、procedure配置
1)开环的节气门开度控制-油门
2)开环的制动主缸压力控制-刹车
3)开环的方向盘角度控制
4)运行条件选择Run forver
3、Run Control配置
1)选择Transfer to Local Windows Directory
2)选择工作目录,即simfile要保存的地方
4、受控车辆名称配置
车辆名称配置:VSC_VEHICLE_NAME Vs Vehicle 0 ,以便我们客户端定义需要控制的车辆
四、VS Connect Server代码
1、打开Sln工程
1、首先下载Visual Studio(我用的是2022和2019),打开Carsim项目工程路径,如:C:\Program Files (x86)\CarSim2022.1_Data\Extensions\Custom_C\wrapper_live_animation
中的wrapper_live_animation.sln
;
2、代码修改
1)为什么要修改工程?
因为Carsim官方的案例只支持从Carsim输出变量,不支持输入变量到Carsim,因此需要增加接收Client发送过来的输入变量;打开vs_connect_live_animation.cpp
代码进行修改
2)修改Build()函数,在创建Node节点的时候增加ReceiveUpdateCallback回调函数;
VscNode node = msVscApi->Node_Create_UDPIP( mListenHost.c_str() // listenAddress
, mListenPort // listenPort
, mMaxConnections // maxConnections
, VSC_TSM_NONE // requiredTsMode
, LinkConnectCallback // linkConnectCallback
, LinkDisconnectCallback // linkdisconnectCallback
, ContractRequestCallback // contractRequestCallback
, ContractCanceledCallback // contractCanceledCallback
, SendUpdateCallback // sendUpdateCallback
, ReceiveUpdateCallback // receiveUpdateCallback
, NULL // pingResultsCallback
, &tempResult // out_result
);
3)修改ContractRequestCallback()函数,增加接收Client端传输过来的变量,在if ( VSC_ROLE_SENDER != VsLiveAnimation::GetVscApi()->Contract_GetLocalRole(contract) )
条件中增加此部分代码;
//外部传入
sLogFunc("Incomming Contract request received... ");
//获取Link链接
const auto node = VsLiveAnimation::GetVscApi()->Link_GetNode(link);
//获取车辆Handle
auto veh = (VSEW_VEHICLE_HANDLE)VsLiveAnimation::GetVscApi()->Node_GetAppData(node);
if (!veh || !vsew_IsOk(veh))
{
sLogFunc("Contract cannot be processed because vehicle is not OK. ");
retRes = VSC_RES_ERROR_UNAVAILABLE;
}
else
{
// Find each solver variable named in the Schema of this contract.
auto schema = VsLiveAnimation::GetVscApi()->Contract_GetSchema(contract);
const auto numFields = VsLiveAnimation::GetVscApi()->Schema_GetNumFields(schema);
retRes = VSC_RES_ERROR_INVALID_DATA; // We'll return this error (rejecting the Contract) if we don't find any solver variables to send.
for (int i = 0; i < numFields; ++i)
{
auto field = VsLiveAnimation::GetVscApi()->Schema_GetField(schema, i);
const auto dataType = VsLiveAnimation::GetVscApi()->Field_GetDataType(field);
const auto sizeInBits = VsLiveAnimation::GetVscApi()->Field_GetElementSizeInBits(field);
const auto numElements = VsLiveAnimation::GetVscApi()->Field_GetNumElements(field);
// We only support fields that are a single 64-bit floating point value. Ignore others:
if (VSC_DATATYPE_FLOAT == dataType
&& 64 == sizeInBits
&& 1 == numElements
)
{
//获取Client发送过来的变量信息
const auto objectName = VsLiveAnimation::GetVscApi()->Field_GetObjectName(field);
const auto propertyName = VsLiveAnimation::GetVscApi()->Field_GetPropertyName(field);
const auto Parameters = VsLiveAnimation::GetVscApi()->Field_GetParams(field);
// 将const char* 转换为std::string
string parametersString(Parameters);
// 使用std::stringstream来提取数字部分
stringstream ss(parametersString);
string command;
double Parameters_double;
// 读取第一个字符串(例如 "ADD")
ss >> command;
// 读取第二个字符串(数字部分)
ss >> Parameters_double;
//添加变量到carsim import
vsew_Import_Add3(veh, propertyName, Parameters_double, FALSE, "REPLACE", VSEW_IVA_DEFAULT, VSEW_IVCO_DEFAULT);
const auto varId = vsew_Import_GetId(veh, propertyName);
if (varId >=0)
{
cout << endl;
cout << "Add CARSIM IMPORT VARIABLE: "<< propertyName << " SUCCESS" << endl;
}
else
{
cout << endl;
cout << "CAN NOT ADD VARIABLE: " << propertyName << ",PLEASE CHECK THE RIGHT FORMAT <ADD 0>" << endl;
}
}
}
retRes = VSC_RES_OK; // Returning "OK" accepts this Contract request.
}
4)增加ReceiveUpdateCallback()函数,用于周期地将变量值传入Carsim(可以在SendUpdateCallback()下方的空白处加入次函数);
VscResult ReceiveUpdateCallback( const VscLink link
, const VscContract contract
, const VscRecord out_data)
{
VscResult retRes = VSC_RES_UNDEFINED;
if (VSC_ROLE_SENDER != VsLiveAnimation::GetVscApi()->Contract_GetLocalRole(contract))
{
const auto node = VsLiveAnimation::GetVscApi()->Link_GetNode(link);
auto veh = (VSEW_VEHICLE_HANDLE)VsLiveAnimation::GetVscApi()