-
CAN dbc를 CAN-FD dbc로 변환하면서 배운 것tip 2025. 12. 9. 19:14
dbc conversion from CAN to CAN-FD¶
- CAN dbc 파일을 CAN-FD dbc 파일로 변환한다.
- candb++ 에디터에서 CAN dbc 파일을 열어서, 각 메시지들의 Type과 DLC를 변경하여 CAN-FD dbc로 변환하고자 했다. 메시지의 Type 선택 메뉴에 CAN Standard, CAN Extended 옵션은 있으나, CAN-FD Standard, CAN-FD Extended가 없다. DLC도 8 바이트를 초과하여서는 설정할 수 없다.
- 살펴보니, candb++에서 처음에 dbc를 만들 때부터 CAN인지, CAN-FD 인지를 선택해야 한다.
- CAN dbc 파일을 CAN-FD dbc 파일로 변환하는 방법이 많이 있겠지만, 나는 코파일럿 (claude sonnet 4.5)의 도움으로 CAN dbc를 CAN-FD dbc로 변환하는 코드를 작성해 본다.
In [1]:from pathlib import Path # 파일 경로 처리를 위한 모듈 import cantools # CAN DBC 파일을 처리하기 위한 모듈ldf is not supported xls is not supportedcantools로 CAN 메시지들 CAN-FD 메시지로 변환¶
- can_example.dbc 파일을 이용한다. dbc에는 CAN 메시지가 2개 있다.
In [2]:# dbc 파일을 로드한다. can_dbc_path = Path(r"can_example.dbc") can_db = cantools.database.load_file(can_dbc_path) # 총 메시지수를 출력한다. print(f'총 메시지 수 : {len(can_db.messages)}') print(f'현재 CAN 메시지 수 : {sum(1 for msg in can_db.messages if not msg.is_fd)}') print(f'현재 CAN-FD 메시지 수: {sum(1 for msg in can_db.messages if msg.is_fd)}') # 총 신호수를 출력한다. print(f'총 신호 수 : {sum(len(msg.signals) for msg in can_db.messages)}')총 메시지 수 : 2 현재 CAN 메시지 수 : 2 현재 CAN-FD 메시지 수: 0 총 신호 수 : 18In [3]:# 모든 메시지를 CAN-FD로 변환한다. for msg in can_db.messages: print(f'변환 중인 메시지: {msg.name} (ID: 0x{msg.frame_id:03x})') msg.is_fd = True # CAN-FD로 설정 # 필요시 메시지 길이도 확장 가능 (예: 8 -> 12, 16, 24, 32, 48, 64) msg.length = 32 # 총 메시지수를 출력한다. print('\n\n') print(f'총 메시지 수 : {len(can_db.messages)}') print(f'현재 CAN 메시지 수 : {sum(1 for msg in can_db.messages if not msg.is_fd)}') print(f'현재 CAN-FD 메시지 수: {sum(1 for msg in can_db.messages if msg.is_fd)}') # 총 신호수를 출력한다. print(f'총 신호 수 : {sum(len(msg.signals) for msg in can_db.messages)}')변환 중인 메시지: msg_002 (ID: 0x321) 변환 중인 메시지: msg_001 (ID: 0x123) 총 메시지 수 : 2 현재 CAN 메시지 수 : 0 현재 CAN-FD 메시지 수: 2 총 신호 수 : 18In [4]:# 변환된 데이터를 새로운 DBC 파일로 저장한다. output_path = Path(r"can-fd_example_1.dbc") cantools.database.dump_file(can_db, output_path) print(f'CAN-FD DBC 파일이 저장되었습니다: {output_path}')CAN-FD DBC 파일이 저장되었습니다: can-fd_example_1.dbcIn [5]:# 변환 결과를 확인한다. # 변환된 파일을 다시 로드한다. canfd_db = cantools.database.load_file(output_path) # 메시지를 확인한다. for i, msg in enumerate(canfd_db.messages[:5]): print(f'메시지 {i+1}: {msg.name}') print(f' - Frame ID: 0x{msg.frame_id:03x}') print(f' - Length: {msg.length} bytes') print(f' - CAN-FD: {msg.is_fd}') print()메시지 1: msg_002 - Frame ID: 0x321 - Length: 32 bytes - CAN-FD: False 메시지 2: msg_001 - Frame ID: 0x123 - Length: 32 bytes - CAN-FD: False발견¶
- CAN-FD 특성을 True로 설정했는데, 바로 위 셀의 출력을 보면 False이다. 왜 그럴까?
- 여기까지 실행한 후 생성된 can-fd_example_1.dbc 파일을 candb++ 에디터에서 열면, 아래 그림과 같이 메시지 Type으로 CAN Standard와 CAN Extended만 선택이 가능하다는 문제가 있다.
- 위 코드 실행으로 DLC가 32로 변경되었다. 그러나 메시지 창을 닫을 때, DLC는 최대 8이라는 오류 경고가 뜨고, DLC가 32로 설정해서는 창이 닫히지 않는다.
조사¶
- candb++ 에디터에서 CAN-FD 템플릿을 이용해서 dbc를 생성한다. can-fd_template.dbc로 저장한다.
- 메인 메뉴/ File/ Create Database를 클릭하여 그림의 Template 창을 연다.
- CAN-FD Template DBC를 선택하여 dbc 파일을 생성한다.
- 같은 방식으로 CAN 템플릿을 이용하여 dbc를 생성한다. can_template.dbc로 저장한다.
- can_template.dbc와 can-fd_template.dbc를 비교하면 아래 그림과 같다.
- CAN-FD 쪽에 CAN 쪽에 없는 속성들이 있다.
- BusType이 각각 CAN-FD, CAN으로 명시되어 있다.
- can_template.dbc와 can-fd_template.dbc에 각각 2개의 메시지들을 추가한다.
- can-fd_template에는 CAN-FD 메시지 1개와 CAN 메시지 1개를 추가한다.
- can_template.dbc에는 CAN 메시지 2개를 추가한다.
결론¶
- cantools로 CAN dbc 메시지의 can-fd 속성을 False에서 True로 변경하고, 메시지 길이를 8에서 32로 변경하는 단순한 방법으로 CAN dbc 파일을 CAN-FD dbc 파일로 변경할 수 없다.
CAN-FD DBC 속성 분석¶
- can-fd_template.dbc 파일에서 CAN-FD 관련 속성을 찾아본다.
In [6]:# CAN-FD 템플릿 파일 로드 canfd_template_path = Path(r"can-fd_template.dbc") canfd_template = cantools.database.load_file(canfd_template_path) # 여러 번의 시행 착오를 통해서 아래 코드를 완성했다. print("CAN-FD 템플릿의 DBC 속성:") print(f" - dbc_specifics: {canfd_template.dbc}") if canfd_template.dbc: print("\n - 전역 속성:") for attr_name, attr_value in canfd_template.dbc.attributes.items(): print(f" {attr_name}: {attr_value}") print("\n - 속성 정의:") # attributes 타입에 따라 처리를 다르게 한다. if isinstance(canfd_template.dbc.attribute_definitions, dict): for attr_name, attr_def in canfd_template.dbc.attribute_definitions.items(): print(f" {attr_name}: {attr_def}") else: for attr_def in canfd_template.dbc.attribute_definitions: print(f" {attr_def}") print("\n\n메시지의 속성:") for msg in canfd_template.messages: print(f"메시지: {msg.name}") print(f" - is_fd: {msg.is_fd}") if msg.dbc: print(f" - DBC 속성:") for attr_name, attr_value in msg.dbc.attributes.items(): print(f" {attr_name}: {attr_value}") print()CAN-FD 템플릿의 DBC 속성: - dbc_specifics: <cantools.database.can.formats.dbc_specifics.DbcSpecifics object at 0x000002BEA93AA090> - 전역 속성: BusType: attribute('BusType', CAN FD) DBName: attribute('DBName', canfd_template) - 속성 정의: MultiplexExtEnabled: attribute_definition('MultiplexExtEnabled', No) CANFD_BRS: attribute_definition('CANFD_BRS', 1) DBName: attribute_definition('DBName', ) BusType: attribute_definition('BusType', ) NodeLayerModules: attribute_definition('NodeLayerModules', ) ECU: attribute_definition('ECU', ) CANoeJitterMax: attribute_definition('CANoeJitterMax', 0) CANoeJitterMin: attribute_definition('CANoeJitterMin', 0) CANoeDrift: attribute_definition('CANoeDrift', 0) CANoeStartDelay: attribute_definition('CANoeStartDelay', 0) VFrameFormat: attribute_definition('VFrameFormat', StandardCAN) 메시지의 속성: 메시지: New_Message_2 - is_fd: False - DBC 속성: 메시지: New_Message_1 - is_fd: True - DBC 속성: VFrameFormat: attribute('VFrameFormat', 14)- dbc에 위 속성(attribute)들이 있다.
- CAN-FD 관련하여 아래 속성들이 눈에 들어온다.
- BusType: attribute('BusType', CAN FD)
- DBName: attribute('DBName', CAN_FD)
- CANFD_BRS: attribute_definition('CANFD_BRS', 1)
- VFrameFormat: attribute_definition('VFrameFormat', StandardCAN)
- New_Message_1이 CAN-FD 메시지다.
- BA_DEF_ BO_ "VFrameFormat" ENUM
- 0: "StandardCAN",
- 1: "ExtendedCAN",
- 2: "reserved",
- 3: "reserved",
- 4: "reserved",
- 5: "reserved",
- 6: "reserved",
- 7: "reserved",
- 8: "reserved",
- 9: "reserved",
- 10: "reserved",
- 11: "reserved",
- 12: "reserved",
- 13: "reserved",
- 14: "StandardCAN_FD",
- 15: "ExtendedCAN_FD";
- BA_ "VFrameFormat" BO_ 0 14;
- 어떻게 어떤 메시지가 StandardCAN이고 어떤 메시지가 StandardCAN_FD를 표시하는 것인지 나는 모르겠다.
- 하지만 Claude가 알아낸다.
올바른 CAN dbc to CAN-FD dbc 변환¶
- Claude가 CAN dbc에서 CAN-FD dbc로 바꾸는 방법을 찾았다.
- VFrameFormat 속성을 추가하여 각 메시지를 CAN-FD로 설정합니다.
- CANFD_BRS 속성도 추가합니다.
- BusType을 "CAN FD"로 설정합니다.
In [7]:# can_example.dbc 파일을 다시 로드한다. can_dbc_path = Path(r"can_example.dbc") can_db = cantools.database.load_file(can_dbc_path) # CAN-FD 템플릿에서 필요한 속성 정의를 복사 canfd_template_path = Path(r"can-fd_template.dbc") canfd_template = cantools.database.load_file(canfd_template_path) # DBC 속성 정의가 없으면 생성 if can_db.dbc is None: from cantools.database.can.formats import dbc as dbc_module can_db._dbc = dbc_module.DbcSpecifics() # 템플릿에서 필요한 속성 정의를 복사 if canfd_template.dbc and canfd_template.dbc.attribute_definitions: # attribute_definitions가 딕셔너리인 경우 if isinstance(canfd_template.dbc.attribute_definitions, dict): for attr_name in ['VFrameFormat', 'CANFD_BRS', 'BusType']: if attr_name in canfd_template.dbc.attribute_definitions: # 기존 정의가 없으면 추가 if attr_name not in can_db.dbc.attribute_definitions: can_db.dbc.attribute_definitions[attr_name] = canfd_template.dbc.attribute_definitions[attr_name] else: # 리스트인 경우 for attr_def in canfd_template.dbc.attribute_definitions: if hasattr(attr_def, 'name') and attr_def.name in ['VFrameFormat', 'CANFD_BRS', 'BusType']: # 기존 정의 확인 existing = [ad for ad in can_db.dbc.attribute_definitions if hasattr(ad, 'name') and ad.name == attr_def.name] if not existing: can_db.dbc.attribute_definitions.append(attr_def) # 전역 BusType 속성을 템플릿에서 복사 (Attribute 객체로) if 'BusType' in canfd_template.dbc.attributes: can_db.dbc.attributes['BusType'] = canfd_template.dbc.attributes['BusType'] else: # Attribute 객체 생성 from cantools.database.can.attribute import Attribute can_db.dbc.attributes['BusType'] = Attribute(value='CAN FD', definition=can_db.dbc.attribute_definitions.get('BusType')) print(f'속성 정의 복사 완료') print(f'현재 속성 정의: {list(can_db.dbc.attribute_definitions.keys()) if isinstance(can_db.dbc.attribute_definitions, dict) else len(can_db.dbc.attribute_definitions)}') print(f'BusType 속성: {can_db.dbc.attributes.get("BusType")}')속성 정의 복사 완료 현재 속성 정의: ['GenMsgCycleTime', 'NmNode', 'NmStationAddress', 'GenMsgCycleTimeFast', 'GenMsgILSupport', 'GenMsgNrOfRepetition', 'GenMsgSendType', 'NWM-Botschaft', 'NmMessage', 'VFrameFormat', 'CANFD_BRS', 'BusType'] BusType 속성: attribute('BusType', CAN FD)In [8]:# 모든 메시지를 CAN-FD로 변환 from cantools.database.can.attribute import Attribute for msg in can_db.messages: msg.is_fd = True # is_fd 플래그 설정 msg.length = 16 # 메시지 길이 확장 # DBC 속성이 없으면 생성 if msg.dbc is None: from cantools.database.can.formats import dbc msg._dbc = dbc.DbcSpecifics() # VFrameFormat 속성 설정 (Standard 또는 Extended) - Attribute 객체로 vframe_value = 'ExtendedCAN_FD' if msg.is_extended_frame else 'StandardCAN_FD' vframe_def = can_db.dbc.attribute_definitions.get('VFrameFormat') msg.dbc.attributes['VFrameFormat'] = Attribute(value=vframe_value, definition=vframe_def) # CANFD_BRS 속성 설정 (Bit Rate Switch 활성화) - Attribute 객체로 brs_def = can_db.dbc.attribute_definitions.get('CANFD_BRS') msg.dbc.attributes['CANFD_BRS'] = Attribute(value='1', definition=brs_def) print(f'변환 완료:') print(f' - 총 메시지 수 : {len(can_db.messages)}') print(f' - CAN-FD 메시지 수: {sum(1 for msg in can_db.messages if msg.is_fd)}') print(f' - StandardCAN_FD: {sum(1 for msg in can_db.messages if msg.dbc and msg.dbc.attributes.get("VFrameFormat") and msg.dbc.attributes.get("VFrameFormat").value == "StandardCAN_FD")}') print(f' - ExtendedCAN_FD: {sum(1 for msg in can_db.messages if msg.dbc and msg.dbc.attributes.get("VFrameFormat") and msg.dbc.attributes.get("VFrameFormat").value == "ExtendedCAN_FD")}')변환 완료: - 총 메시지 수 : 2 - CAN-FD 메시지 수: 2 - StandardCAN_FD: 2 - ExtendedCAN_FD: 0In [9]:# CAN-FD DBC 파일로 저장 output_path_v2 = Path(r"can-fd_example_2.dbc") cantools.database.dump_file(can_db, output_path_v2) print(f'CAN-FD DBC 파일이 저장되었습니다: {output_path_v2}')CAN-FD DBC 파일이 저장되었습니다: can-fd_example_2.dbc변환된 파일 검증¶
- 저장된 파일을 다시 로드하여 속성들이 제대로 저장되었는지 확인한다.
In [11]:# 변환된 파일 다시 로드 verify_db = cantools.database.load_file(output_path_v2) # 총 메시지수를 출력한다. print(f'검증 결과:') print(f'총 메시지 수 : {len(can_db.messages)}') print(f'현재 CAN 메시지 수 : {sum(1 for msg in can_db.messages if not msg.is_fd)}') print(f'현재 CAN-FD 메시지 수: {sum(1 for msg in can_db.messages if msg.is_fd)}') # 총 신호수를 출력한다. print(f'총 신호 수 : {sum(len(msg.signals) for msg in can_db.messages)}') print() # 처음 3개 메시지 확인 print('메시지 확인:') for i, msg in enumerate(verify_db.messages): print(f'\n메시지 {i+1}: {msg.name} (ID: 0x{msg.frame_id:03x})') print(f' - Length: {msg.length} bytes') # dlc를 출력한다. print(f' - is_fd: {msg.is_fd}') print(f' - is_extended_frame: {msg.is_extended_frame}') if msg.dbc: print(f' - VFrameFormat: {msg.dbc.attributes.get("VFrameFormat", "N/A")}') print(f' - CANFD_BRS: {msg.dbc.attributes.get("CANFD_BRS", "N/A")}')검증 결과: 총 메시지 수 : 2 현재 CAN 메시지 수 : 0 현재 CAN-FD 메시지 수: 2 총 신호 수 : 18 메시지 확인: 메시지 1: msg_002 (ID: 0x321) - Length: 16 bytes - is_fd: True - is_extended_frame: False - VFrameFormat: attribute('VFrameFormat', 14) - CANFD_BRS: attribute('CANFD_BRS', 1) 메시지 2: msg_001 (ID: 0x123) - Length: 16 bytes - is_fd: True - is_extended_frame: False - VFrameFormat: attribute('VFrameFormat', 14) - CANFD_BRS: attribute('CANFD_BRS', 1)결과¶
- can_example.dbc를 변환한 can-fd_example.dbc를 candb++ 에디터에서 열어본다.
- Type이 CAN FD Standard로 잘 바뀌었다.
- DLC는 16으로 잘 바뀌었다.
'tip' 카테고리의 다른 글
dbc2xlsx (3) 2025.08.29 dbc 파일에서 바이트 오더와 스타트 비트 위치와 LSB (1) 2025.08.27 dbc 파일 인코딩 (encoding) (3) 2025.08.26 xlsx2dbc (1) 2025.08.22 YouTube: 미니프로그램에서 엑셀 파일 다루는 방법 (0) 2025.08.19