Carsim高级开发:VS Connect通讯开发指南


前言

为什么要使用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”。
VscSchemaVscField 的有序集合。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()->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
          )
        {
          const auto propertyName = VsLiveAnimation::GetVscApi()->Field_GetPropertyName(field);
          double* fieldData = (double*)VsLiveAnimation::GetVscApi()->Record_GetFieldValue(out_data, i);

          //get carsim variable id
          const auto varId = vsew_Import_GetId(veh, propertyName);

          if (varId >= 0)
          {
            //set init import value
            vsew_Import_SetValue(veh, varId, *fieldData);
          }
        }
      }
      retRes = VSC_RES_OK;  // Returning "OK" accepts this Contract request.
    }

  }
  return retRes;
}

VS Connect Server就加这部分代码。


五、VS Connect Client代码

1、函数的调用关系

代码已上传资源,carsim vs-connect-client代码
在这里插入图片描述

2、carsim_variable代码

1)这里就是用来实现carsim变量定义,carsim输入变量请求,carsim输出变量获取;

2)Field变量定义的规则
incoming
在这里插入图片描述

outgoing
在这里插入图片描述


import ctypes
from ctypes import *

import VsConnectAPI

vscapi = VsConnectAPI.vsc_dll.VsConnectApi_Get  # VsConnectApi_GetDefault

vscapi.argtypes=[ctypes.c_int]
vscapi.restype = ctypes.POINTER(VsConnectAPI.Vsc_API)

api_ver3 = vscapi(3)
api_ver3_contents = api_ver3.contents


def StrToChar(string):
    string_utf8 = string.encode('UTF-8')
    string_buffer = ctypes.create_string_buffer(string_utf8)
    return ctypes.cast(string_buffer, c_char_p)

def ResetData():
    global gx
    global gy
    global gz
    global gpitch
    global gyaw
    global groll

    ref_x = ctypes.POINTER(ctypes.c_double)
    api_ver3_contents.InvalidateDouble(ref_x(ctypes.c_double(gx)))
    ref_y = ctypes.POINTER(ctypes.c_double)
    api_ver3_contents.InvalidateDouble(ref_y(ctypes.c_double(gy)))
    ref_z = ctypes.POINTER(ctypes.c_double)
    api_ver3_contents.InvalidateDouble(ref_z(ctypes.c_double(gz)))
    ref_pitch = ctypes.POINTER(ctypes.c_double)
    api_ver3_contents.InvalidateDouble(ref_pitch(ctypes.c_double(gpitch)))
    ref_yaw = ctypes.POINTER(ctypes.c_double)
    api_ver3_contents.InvalidateDouble(ref_yaw(ctypes.c_double(gyaw)))
    ref_roll = ctypes.POINTER(ctypes.c_double)
    api_ver3_contents.InvalidateDouble(ref_roll(ctypes.c_double(gyaw)))

def SendUpdate(out_data):
    # IMP_THROTTLE_ENGINE
    fieldData = ctypes.cast(api_ver3_contents.RecordGetFieldValue(out_data, 0), ctypes.POINTER(ctypes.c_double))
    fieldData[0] = 0.1
    fieldData1 = ctypes.cast(api_ver3_contents.RecordGetFieldValue(out_data, 1), ctypes.POINTER(ctypes.c_double))
    fieldData1[0] = 0

def ReceiveUpdate(incomingData):
    global gx
    global gy
    global gz
    global gpitch
    global gyaw
    global groll

    gx = ((ctypes.cast(api_ver3_contents.RecordGetFieldValue(incomingData, 0), ctypes.POINTER(ctypes.c_double))).contents).value  # return pointer double
    gy = ((ctypes.cast(api_ver3_contents.RecordGetFieldValue(incomingData, 1), ctypes.POINTER(ctypes.c_double))).contents).value  # return pointer double
    gz = ((ctypes.cast(api_ver3_contents.RecordGetFieldValue(incomingData, 2), ctypes.POINTER(ctypes.c_double))).contents).value  # return pointer double
    gpitch = ((ctypes.cast(api_ver3_contents.RecordGetFieldValue(incomingData, 3), ctypes.POINTER(ctypes.c_double))).contents).value  # return pointer double
    gyaw = ((ctypes.cast(api_ver3_contents.RecordGetFieldValue(incomingData, 4), ctypes.POINTER(ctypes.c_double))).contents).value  # return pointer double
    groll = ((ctypes.cast(api_ver3_contents.RecordGetFieldValue(incomingData, 5), ctypes.POINTER(ctypes.c_double))).contents).value  # return pointer double


def PrintStatementWhenConnected():
    print("Dist x: %.2f" % gx, end=" ") if api_ver3_contents.IsValidDouble(gx) \
        else print("Dist x:?")
    print("Dist y: %.2f" % gy, end=" ") if api_ver3_contents.IsValidDouble(gy) \
        else print("Dist y:?")
    print("Dist z: %.2f" % gz, end=" ") if api_ver3_contents.IsValidDouble(gz) \
        else print("Dist z:?")
    print("pitch: %.2f" % gpitch, end=" ") if api_ver3_contents.IsValidDouble(
        gpitch) \
        else print("pitch:?")
    print("yaw: %.2f" % gyaw) if api_ver3_contents.IsValidDouble(gyaw) \
        else print("yaw:?")
    print("roll: %.2f" % groll) if api_ver3_contents.IsValidDouble(groll) \
        else print("roll:?")


# define variables
gx = 0.0
gy = 0.0
gz = 0.0

gpitch = 0.0
gyaw = 0.0
groll = 0.0

#定义Carsim输出变量
incoming_variables = [
    {"objectName": StrToChar("Vs Vehicle 0"),  "propertyName": StrToChar("Xo"), "propertyParam": StrToChar("")},
    {"objectName": StrToChar("Vs Vehicle 0"),  "propertyName": StrToChar("Yo"), "propertyParam": StrToChar("")},
    {"objectName": StrToChar("Vs Vehicle 0"),  "propertyName": StrToChar("Zo"), "propertyParam": StrToChar("")},
    {"objectName": StrToChar("Vs Vehicle 0"),  "propertyName": StrToChar("pitch"), "propertyParam": StrToChar("")},
    {"objectName": StrToChar("Vs Vehicle 0"),  "propertyName": StrToChar("Yaw"), "propertyParam": StrToChar("")},
    {"objectName": StrToChar("Vs Vehicle 0"),  "propertyName": StrToChar("roll"), "propertyParam": StrToChar("")},
]

#定义Carsim输入变量
outgoing_variables = [
    {"objectName": StrToChar("Vs Vehicle 0"),  "propertyName": StrToChar("IMP_THROTTLE_ENGINE"), "propertyParam": StrToChar("ADD 0.0")},
    {"objectName": StrToChar("Vs Vehicle 0"),  "propertyName": StrToChar("IMP_PCON_BK"), "propertyParam": StrToChar("ADD 0.0")}
]

3、VsConnect_demo代码

1)这里就是client端的主代码,具体作用请查看《二、VS Connect 通讯框架》

import sys
import platform
import ctypes
from ctypes import *
import time

import VsConnectAPI
from VsConnectAPI import *
import carsim_variable

current_os = platform.system()
if (current_os == "Linux"):
    import termios
    import select
    import atexit

    # save the terminal settings
    fd = sys.stdin.fileno()
    new_term = termios.tcgetattr(fd)
    old_term = termios.tcgetattr(fd)

    # new terminal setting unbuffered
    new_term[3] = (new_term[3] & ~termios.ICANON & ~termios.ECHO)

    # switch to normal terminal
    def set_normal_term():
        termios.tcsetattr(fd, termios.TCSAFLUSH, old_term)

    # switch to unbuffered terminal
    def set_curses_term():
        termios.tcsetattr(fd, termios.TCSAFLUSH, new_term)

    def kbhit():
        dr,dw,de = select.select([sys.stdin], [], [], 0)
        return dr != []

    def getch():
        return sys.stdin.read(1).encode('utf-8')


else:
    import msvcrt
    def kbhit():
        return msvcrt.kbhit()

    def getch():
        return msvcrt.getch()


gWantConnection = True
gQuit = False


if (current_os == "Linux"):
    import ctypes.util
    pathlib = ctypes.util.find_library("c")
    libc = ctypes.cdll.LoadLibrary(pathlib)
    vsprintf = libc.vsprintf
else:
    vsprintf = ctypes.cdll.msvcrt.vsprintf


def logcallback_fnc(loglevel, node, link, contract, format, argptr):
    logPrefix = ""
    byteString = create_string_buffer(4096)
    realString = ""

    if (loglevel == VsConnectAPI.VscLogLevel.VSC_LOG_ERROR.value):
        logPrefix = "ERROR"
    elif (loglevel == VsConnectAPI.VscLogLevel.VSC_LOG_WARNING.value):
        logPrefix = "WARNING"
    else:
        logPrefix = "Log"

    byteString = "VS Connect " + logPrefix
    realString += byteString.value.decode("utf-8")

    if (node or link or contract):
        realString += ","

        if (node):
            byteString = " N-" + str(hex(ctypes.addressof(node)))
            realString += byteString.value.decode("utf-8")

        if (link):
            byteString = " L-" + str(hex(ctypes.addressof(link)))
            realString += byteString.value.decode("utf-8")

        if (contract):
            byteString = " C-" + str(hex(ctypes.addressof(contract)))
            realString += byteString.value.decode("utf-8")

    realString += ": "

    # python casted format and argptr to ints. recast to c_void_p
    vsprintf(byteString,  ctypes.cast(format, c_void_p), ctypes.cast(argptr, c_void_p))
    realString += byteString.value.decode("utf-8")

    print(realString)

    return len(realString)


def ProcessKeyboardInput():
    if kbhit():
        pressedKey = getch()
        global gWantConnection
        global gQuit

        if (pressedKey == b'q') or (pressedKey == b'Q'):
            gWantConnection = False
            gQuit = True
        elif (pressedKey == b'd') or (pressedKey == b'D'):
            gWantConnection = False
        elif (pressedKey == b'c') or (pressedKey == b'C'):
            gWantConnection = True
        else:
            print("Unknown command.\n")
            print("Press C to connect, D to disconnect, Q to quit.\n")



@CFUNCTYPE(ctypes.c_int, ctypes.POINTER(VsConnectAPI.VscLink), ctypes.POINTER(VsConnectAPI.VscContract), ctypes.POINTER(VsConnectAPI.VscRecord))
def sendupdatecallback_fnc(link, contract, out_data):
    if __debug__:
        pass
        # schema = api_ver3_contents.ContractGetSchema(contract)  # check this
        # field = api_ver3_contents.SchemaGetField(schema, 0)
        # assert (1 == api_ver3_contents.SchemaGetNumFields(schema))
        # assert (1 == api_ver3_contents.FieldGetDataTypes(field))
        # assert (1 == api_ver3_contents.FieldGetNumElements(field))

    carsim_variable.SendUpdate(out_data)

    return int(VsConnectAPI.VscResult.VSC_RES_OK)


@CFUNCTYPE(ctypes.c_int, ctypes.POINTER(VsConnectAPI.VscLink), ctypes.POINTER(VsConnectAPI.VscContract), ctypes.POINTER(VsConnectAPI.VscRecord))
def receiveupdatecallback_fnc(link, contract, incomingData):
    if __debug__:
        pass
        # schema = api_ver3_contents.ContractGetSchema(contract)
        # assert (2 == api_ver3_contents.SchemaGetNumFields(schema))
        # assert (1 == api_ver3_contents.FieldGetDataTypes(api_ver3_contents.SchemaGetField(schema, 0)))
        # assert (1 == api_ver3_contents.FieldGetNumElements(api_ver3_contents.SchemaGetField(schema, 0)))
        # assert (1 == api_ver3_contents.FieldGetDataTypes(api_ver3_contents.SchemaGetField(schema, 1)))
        # assert (1 == api_ver3_contents.FieldGetNumElements(api_ver3_contents.SchemaGetField(schema, 1)))

    carsim_variable.ReceiveUpdate(incomingData)

    return int(VsConnectAPI.VscResult.VSC_RES_OK)

def get_VsConnectApi():
    # accessing api_ver3
    vscapi = VsConnectAPI.vsc_dll.VsConnectApi_Get  # VsConnectApi_GetDefault
    vscapi.argtypes = [ctypes.c_int]
    vscapi.restype = ctypes.POINTER(VsConnectAPI.Vsc_API)
    # initialize the VS connect library.
    api_ver3 = vscapi(3)
    api_ver3_contents = api_ver3.contents
    return api_ver3_contents

def create_link(address, portnum):
    address_buf = address.encode('UTF-8')
    address_char = ctypes.cast(address_buf, c_char_p)
    portnum_int = ctypes.c_int(portnum)
    glink = api_ver3_contents.LinkCreateUDPIP(address_char, portnum_int, cast(None, ctypes.POINTER(c_int)))
    return glink

def create_SchemaInitField(variables):
    Schema = api_ver3_contents.SchemaCreate((len(variables)))
    for i, variable in enumerate(variables):
        api_ver3_contents.SchemaInitField(Schema, i, 1, 64, 1, variable.get("objectName"), variable.get("propertyName"), variable.get("propertyParam"), 0, 0, 0)
    return Schema

def create_IncomingContract(glink, Schema, UpdatePeriod_ms):
    to_c_double= ctypes.c_double
    UpdatePeriod = to_c_double(UpdatePeriod_ms)
    UpdateDef = api_ver3_contents.UpdateDefinitionCreate(Schema, 1, UpdatePeriod)  #dfefind update period
    api_ver3_contents.LinkCreateIncomingContract(glink, UpdateDef, receiveupdatecallback_fnc, 0, -1, None)
    return UpdateDef

def create_OutgoingContract(glink, Schema, UpdatePeriod_ms):
    to_c_double= ctypes.c_double
    UpdatePeriod = to_c_double(UpdatePeriod_ms)
    UpdateDef = api_ver3_contents.UpdateDefinitionCreate(Schema, 1, UpdatePeriod)  #dfefind update period
    api_ver3_contents.LinkCreateOutgoingContract(glink, UpdateDef, sendupdatecallback_fnc, 0, -1, None)
    return UpdateDef

def release_DataHandle(Schema, UpdateDef):
    ref_Schema = ctypes.POINTER(ctypes.POINTER(VsConnectAPI.VscSchema))
    ref_UpdateDef = ctypes.POINTER(ctypes.POINTER(VsConnectAPI.VscUpdateDefinition))
    api_ver3_contents.SchemaHandleRelease(ref_Schema(Schema))
    api_ver3_contents.UpdateDefinitionHandleRelease(ref_UpdateDef(UpdateDef))

def sync_LinkConnect(node, glink):
    #If it is not synchronized, synchronize it
    if ((VsConnectAPI.VscConnectionStatus.VSC_CONNSTAT_UNCONNECTED.value == connStat) and gWantConnection):
        res = api_ver3_contents.LinkConnectAsync(node, glink)
        res_val = api_ver3_contents.DescribeResult(res)
        print('Initiating connection: %s' % res_val.decode("utf-8"))

    elif (((VsConnectAPI.VscConnectionStatus.VSC_CONNSTAT_CONNECTED.value == connStat) and not gWantConnection) or \
         (( VsConnectAPI.VscConnectionStatus.VSC_CONNSTAT_CONNECTING.value == connStat) and not gWantConnection)):
        res = api_ver3_contents.LinkDisconnect(glink)
        print("Initiating disconnection: %s", api_ver3_contents.DescribeResult(res))

def run_NodeService(localSimTime):
    localSimTimeDouble = ctypes.c_double
    localSimTimeInput = localSimTimeDouble(localSimTime)
    timeDilationDouble = ctypes.c_double
    timeDilation = timeDilationDouble(1.0)
    processEventBool = ctypes.c_bool
    processEventInput = processEventBool(True)
    api_ver3_contents.NodeService(node, localSimTimeInput, timeDilation, processEventInput)

def release_NetWorkHandle(glink, node):
    ref_glink = ctypes.POINTER(ctypes.POINTER(VsConnectAPI.VscLink))
    ref_node = ctypes.POINTER(ctypes.POINTER(VsConnectAPI.VscNode))
    api_ver3_contents.LinkHandleRelease(ref_glink(glink))
    api_ver3_contents.NodeHandleRelease(ref_node(node))
    api_ver3_contents.ShutDown()

# main start here...

api_ver3_contents = get_VsConnectApi()

# Set logging function
VsConnectAPI.SetLogFuncPtr(logcallback_fnc)

api_ver3_contents.Init()

node = api_ver3_contents.NodeCreateUDPIP(   None, #None则自动选择IPv4 ip
                                            0,  #0则自动通过GetDefaultListenPort()获取
                                            -1,
                                            0, 
                                            cast(None, VscLinkConnectedFunc_t), 
                                            cast(None, VscLinkDisconnectedFunc_t), 
                                            cast(None, VscProcessContractRequestFunc_t), 
                                            cast(None, VscContractCanceledFunc_t), 
                                            sendupdatecallback_fnc, 
                                            receiveupdatecallback_fnc, 
                                            cast(None, VscPingResultFunc_t), 
                                            cast(None, ctypes.POINTER(c_int))
                                        )

glink = create_link("127.0.0.1", 4380)

update_period = 50

# this can be loop?
incomingSchema = create_SchemaInitField(carsim_variable.incoming_variables)
incomingUpdateDef = create_IncomingContract(glink, incomingSchema, update_period)
release_DataHandle(incomingSchema, incomingUpdateDef)

# now create outgoing contract
# this can be loop?
outgoingSchema = create_SchemaInitField(carsim_variable.outgoing_variables)
outgoingUpdateDef = create_OutgoingContract(glink, outgoingSchema, update_period)
release_DataHandle(outgoingSchema, outgoingUpdateDef)

print("Press C to connect, D to disconnect, or Q to quit.")

# carsim.ResetData()

timeStep = update_period / 1000
timeStepCount = 0
localSimTime = 0.0

if (current_os == "Linux"):
    atexit.register(set_normal_term)
    set_curses_term()

# # while statement will wrap the following statements
while (not gQuit):
    timeStepCount += 1
    connStat = api_ver3_contents.LinkGetConnectionStatus(glink)

    #sync link connect 
    sync_LinkConnect(node, glink)

    #运行NodeService,数据收发/回调参数处理
    run_NodeService(localSimTime)

    if (0 == timeStepCount % 20):  # reduce output to every nth iteration
        print("%.2f" % localSimTime)
        if (connStat == VsConnectAPI.VscConnectionStatus.VSC_CONNSTAT_UNCONNECTED.value):
            print("Disconnected ")
        elif (connStat == VsConnectAPI.VscConnectionStatus.VSC_CONNSTAT_CONNECTING.value):
            print("Connecting ")
        elif (connStat == VsConnectAPI.VscConnectionStatus.VSC_CONNSTAT_CONNECTED.value):
            # print("Connected. ")
            carsim_variable.PrintStatementWhenConnected()
        elif (connStat == VsConnectAPI.VscConnectionStatus.VSC_CONNSTAT_ERROR.value):
            print("ERROR ")
        elif (connStat == VsConnectAPI.VscConnectionStatus.VSC_CONNSTAT_DISCONNECTING.value):
            print("Disconnectcing ")
        else:
            print("************** ")
            # a primative timing mechanism.  Should be used with this example only.

    time.sleep(timeStep)
    localSimTime += timeStep
    ProcessKeyboardInput()

print("Program exiting, disconnecting link (if it's connected).")
api_ver3_contents.LinkDisconnect(glink)

while (VsConnectAPI.VscConnectionStatus.VSC_CONNSTAT_UNCONNECTED.value != api_ver3_contents.LinkGetConnectionStatus(glink)):
    print(".")
    api_ver3_contents.NodeService(node, api_ver3_contents.GetInvalidSimtime(), 1, True)


release_NetWorkHandle(glink, node)

六、Vs Connect联合仿真

1、Server代码编译

在这里插入图片描述
在这里插入图片描述

编译之后有个wrapper_live_animation_Release_x64.exe
在这里插入图片描述

2、运行carsim

在这里插入图片描述

3、运行Server端

.\wrapper_live_animation_Release_x64.exe “C:\Program Files (x86)\CarSim2022.1_Data\simfile.sim” -host “127.0.0.1” -port 4380

IP 和端口号都在Client代码中定义了
在这里插入图片描述
运行Server之后,显示连接数量为0
在这里插入图片描述

4、运行Client端

成功输出我定义的变量,哗啦啦的!

在这里插入图片描述


综上,完成Carsim Vs Connect demo开发;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/712971.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【JAVA开发笔记】实战演练,如何用EasyExcel导出表格,并且自定义合并单元格

目录 1. 前言 2. EasyExcel简介 3. EasyExcel简单导出案例讲解 3.1 EasyExcel依赖引入 3.2 测试类创建 3.3 Excel导出实现 4. EasyExcel合并单元案例讲解 4.1 实现自定义合并策略 4.2 使用自定义合并策略 5. 总结 1. 前言 项目上&#xff0c;需将一个列表数据导出Ex…

16. 第十六章 类和函数

16. 类和函数 现在我们已经知道如何创建新的类型, 下一步是编写接收用户定义的对象作为参数或者将其当作结果用户定义的函数. 本章我会展示函数式编程风格, 以及两个新的程序开发计划.本章的代码示例可以从↓下载. https://github.com/AllenDowney/ThinkPython2/blob/master/c…

(源码)供应商电子招投标管理系统实现方案和功能说明

采购在线招投标供应商管理系统是一个集成了多个关键功能的综合性系统&#xff0c;旨在优化采购流程、提高效率和确保透明度。以下是关于您提到的五个核心功能的详细解释&#xff1a; 供应商管理 此功能允许企业记录和管理供应商的基本信息&#xff0c;如公司名称、联系方式、主…

了解并解决 Flutter 中的灰屏问题

生产中的 flutter 应用程序中的灰屏是一种通用占位符&#xff0c;当框架遇到问题无法渲染预期用户界面时就会显示。是的&#xff0c;所以基本上是出现问题时的后备指示器。 有趣的是&#xff0c;这只出现在发布模式下。在任何其他模式下运行都会显示红色错误屏幕&#xff0c;并…

apt-get update和apt-get upgrade的区别

apt-get update apt-get update 命令用于更新本地软件包列表。具体来说&#xff0c;做了以下事情&#xff1a; ①从 /etc/apt/sources.list 文件和 /etc/apt/sources.list.d/ 目录下的所有文件中读取软件源配置。 ②连接到这些软件源&#xff0c;并下载最新的软件包列表。 ③将…

前端老古董execCommand——操作 选中文本 样式

文章目录 ⭐前言⭐exe command api用法&#x1f496; example示例&#x1f496; 测试效果 ⭐execommand和getSelection 的联系⭐总结⭐结束 ⭐前言 大家好&#xff0c;我是yma16&#xff0c;本文分享关于 前端老古董execCommand——操作选中文本。 execommand 当一个 HTML 文…

【Linux】进程_4

文章目录 五、进程4. 进程状态5. 进程优先级6. 进程的调度和转换 未完待续 五、进程 4. 进程状态 当进程属于挂起状态时&#xff0c;进程的可执行程序代码和数据均会被从内存中换入到磁盘中&#xff0c;此时进程的PCB并没有消失&#xff0c;只要操作系统还需要管理这个进程&a…

ChatGPT关联技术

ChatGPT关联技术 一、前馈神经网络二、序列到序列模型&#xff08;Seq2Seq&#xff09;三、自注意力机制四、多头自注意力机制五、自监督学习六、Transformer模型七、语言生成技术八、多语种语言模型九、预训练语言模型十、生成式预训练模型&#xff08;GPT&#xff09;十一、近…

【odoo】odoo.conf文件配置

概要 odoo.conf 文件是 Odoo 服务器的配置文件&#xff0c;它用于定义和管理 Odoo 运行时的各种参数。这个文件包含了许多配置选项&#xff0c;可以帮助管理员根据特定的需求和环境来调整 Odoo 服务器的行为。 主要功能 数据库连接设置&#xff1a;定义 Odoo 连接到 PostgreSQL…

使用tkinter创建带有图标的菜单栏

使用tkinter创建带有图标的菜单栏 效果代码代码解析创建主窗口加载图标创建菜单栏添加文件菜单添加带图标的菜单项 Tkinter 的默认菜单外观较为简单&#xff0c;可以通过自定义和添加图标&#xff0c;让菜单显示更好看。 效果 代码 import tkinter as tk from tkinter import …

【SpringBoot】SpringBoot:构建安全的Web应用程序

文章目录 引言为什么需要安全Spring Security概述配置Spring Security添加依赖基本配置 用户认证创建用户实体类创建用户存储库自定义用户服务更新安全配置 用户授权更新用户实体类更新自定义用户服务更新安全配置 防护措施防止SQL注入使用参数化查询 防止跨站脚本&#xff08;…

Java17 --- RabbitMQ之插件使用

目录 一、Federation插件 1.1、运行两个rabbitmq实例 1.2、启用插件 1.3、在下游端点添加上游端点 1.4、创建策略 1.6、测试 二、联邦队列 2.1、创建策略 2.2、创建交换机与队列 2.2.1、创建52000的队列与交换机 2.2.2、创建62000的队列 三、Shovel 3.1、启…

WNR最便捷美观的开源桌面计时器工具

华丽外观&#xff0c;功能全面。工作和休息的完美计时器。跨平台支持&#xff0c;无论是Windows、Mac还是Linux&#xff0c;WNR都能轻松驾驭。 超强全屏专注模式 对于寻找高效工作/休息管理工具却屡屡受挫的用户&#xff0c;WNR的“全屏专注模式”无疑是终极解决方案。它确保在…

Android 蓝牙配对Settings应用里面的简要流程记录

Android 蓝牙配对Settings应用里面的简要流程记录 文章目录 Android 蓝牙配对Settings应用里面的简要流程记录一、前言二、Settings蓝牙配对的关键代码1、接收蓝牙请求的地方 AndroidManifest.xml2、BluetoothPairingRequest3、BluetoothPairingService4、BluetoothPairingDial…

利用机器学习重构视频中的人脸

引言 中国与英国的研究团队携手合作&#xff0c;开创了一种创新的视频面孔重塑技术。这项技术能够以极高的一致性对视频中的面部结构进行逼真的放大和缩小&#xff0c;且避免了常见伪影的产生。 从研究人员选取的YouTube视频样例中可见&#xff0c;经过处理后&#xff0c;女演…

LC1020:飞地的数量

题目 给你一个大小为 m x n 的二进制矩阵 grid &#xff0c;其中 0 表示一个海洋单元格、1 表示一个陆地单元格。 一次 移动 是指从一个陆地单元格走到另一个相邻&#xff08;上、下、左、右&#xff09;的陆地单元格或跨过 grid 的边界。 返回网格中 无法 在任意次数的移动…

在ubuntu中启动docker的mysql8镜像

首先查看docker是否启动&#xff1a; docker ps #出现信息就是启动成功 启动命令&#xff1a; sudo systemctl start docker 设置开机自启&#xff1a; sudo systemctl enable docker 查询下载好的mysql8的镜像文件&#xff1a; docker images 在启动查询好的镜像文件&#…

Oracle--19C在Centos7上的静默安装(rpm版)

一、Oracle 19c Linux安装&#xff08;Centos 7&#xff09; 1.查看磁盘可用空间及配置ip地址 [rootlocalhost /]# df -h 文件系统 容量 已用 可用 已用% 挂载点 devtmpfs 1.4G 0 1.4G 0% /dev tmpfs 1.4G …

【Pytorch】一文向您详细介绍 model.eval() 的作用和用法

【Pytorch】一文向您详细介绍 model.eval() 的作用和用法 下滑查看解决方法 &#x1f308; 欢迎莅临我的个人主页 &#x1f448;这里是我静心耕耘深度学习领域、真诚分享知识与智慧的小天地&#xff01;&#x1f387; &#x1f393; 博主简介&#xff1a;985高校的普通本硕…

桂电人工智能学院大数据实验,使用 Docker 搭建 hadoop 集群

桂电人工智能学院大数据实验&#xff0c;使用 Docker 搭建 hadoop 集群 第一步 安装 Docker, Windows 上可以使用 Docker Desktop 下载地址&#xff1a;https://www.docker.com/products/docker-desktop/ 安装过程自行谷歌 安装好的标志&#xff1a;打开终端 运行docker p…