BIM ソフトウェア アーキテクチャ: AEC 業界向けの 3D モデリングとコラボレーション
Il ビルディング インフォメーション モデリング (BIM) 世界を根本的に変えた 建築、エンジニアリング、建設 (AEC) が設計、構築、管理する場所 建物。 2024 年には、米国の建築事務所の 66% 以上が BIM ツールを使用しています。 一方、世界の BIM ソフトウェア市場は 79 億 6,000 万ドルと評価されています 2024 年には 336 億 4000 万、2033 年までに 336 億 4000 万になると予測されています (CAGR 17.37%)。
しかし、最新の BIM ソフトウェアの舞台裏では何が起こっているのでしょうか?なんと 3D モデルはメモリ内にありますか?共有モデルではマルチユーザー コラボレーションはどのように機能しますか? そして、クラウドはこれらのシステムの導入にどのような変化をもたらしたのでしょうか?この記事では、 BIM システムの完全なアーキテクチャ、IFC 標準、BCF フォーマットの分析 コラボレーションと 3D Web レンダリングの技術的課題。
何を学ぶか
- IFC 4.3 (ISO 16739-1:2024) 標準と BIM データ スキーマ
- Three.js/WebGL を使用した Web BIM ビューアのアーキテクチャ
- コラボレーション層: BIM コラボレーション フォーマット (BCF) と競合管理
- IFC 解析パイプライン: バイナリ ファイルからレンダリング可能な 3D シーンまで
- 3D交差アルゴリズムによる自動衝突検出
- クラウド BIM: マルチテナント アーキテクチャとジオメトリ ストリーミング
- 建物のデジタルツインのIoTとの統合
IFC 規格: BIM の DNA
L'業界基礎クラス (IFC) オープン データ スキーマです。 異なる BIM ソフトウェア間の相互運用性が可能になります。 IFC 4.3 ADD2 のバージョンは次のとおりです。 ISO 16739-1:2024 規格となり、2024 年に承認されました。開発および維持されています。 ビルディングスマートインターナショナルより。
IFCは、建物の各要素(壁、ドア、窓、 ビーム、フィクスチャ) は、標準のプロパティと関係を持つクラスです。 他の要素で事前定義されています。 3D ジオメトリはメタデータから分離されています STEP 表現 (ISO 10303) による。
| IFCバージョン | Anno | 主な特長 | 状態 |
|---|---|---|---|
| IFC 2x3 | 2006年 | 最初の安定版リリース、基本アーキテクチャ | レガシー(今も人気) |
| IFC4 | 2013年 | 拡張 MEP、高度な構造、4D/5D | 広くサポートされている |
| IFC 4.3 | 2022/2024 | 土木インフラ、鉄道、地盤工学 | ISO 16739-1:2024 (推奨) |
# Esempio di entità IFC in formato STEP (file .ifc)
# Un semplice muro con le sue proprietà e geometria
#100 = IFCPROJECT('2O2Fr$t4X7Dn_hFJ7mkFuC',
$,'Progetto Residenziale Milano',$,$,$,$,$,
(#101,)); # IfcUnitAssignment
#200 = IFCWALL('0LV8Xs0bzEIeVBuDgFJqEY',
$,'Muro Perimetrale Est','Muro in laterizio',$,
#500, # IfcLocalPlacement
#600, # IfcProductDefinitionShape
$,
.SOLIDWALL.);
#300 = IFCWALLTYPE('1Kl0KQPzj5E9XhKaSyTd5Q',
$,'MuroEsterno300mm',$,$,$,$,$,$,
.SOLIDWALL.);
# Proprietà del muro
#400 = IFCPROPERTYSET('3ZYW59saX3kxpqB_7Pnv3D',
$,'Pset_WallCommon',$,
(#401,#402,#403,#404));
#401 = IFCPROPERTYSINGLEVALUE('Reference',$,
IFCIDENTIFIER('MW-300'),$);
#402 = IFCPROPERTYSINGLEVALUE('IsExternal',$,
IFCBOOLEAN(.T.),$);
#403 = IFCPROPERTYSINGLEVALUE('LoadBearing',$,
IFCBOOLEAN(.T.),$);
#404 = IFCPROPERTYSINGLEVALUE('FireRating',$,
IFCLABEL('REI120'),$);
ifcopenshell を使用した Python の IFC パーサー
オープンソース ライブラリ イフコペンシェル 標準リファレンスです IFC ファイルの読み取り、編集、作成を行います。 IFC 2x3、IFC 4、および IFC 4.3 をサポートします。
import ifcopenshell
import ifcopenshell.geom
import numpy as np
from dataclasses import dataclass, field
from typing import List, Dict, Optional
@dataclass
class BIMElement:
"""Rappresentazione unificata di un elemento BIM."""
global_id: str
ifc_type: str
name: str
properties: Dict[str, any] = field(default_factory=dict)
geometry: Optional[dict] = None
level: Optional[str] = None
classification: Optional[str] = None
class IFCParser:
"""
Parser IFC per estrarre elementi, proprietà e geometrie.
Supporta IFC 2x3, 4 e 4.3.
"""
# Tipologie di elementi architettonici da estrarre
SPATIAL_ELEMENTS = [
'IfcBuilding', 'IfcBuildingStorey', 'IfcSpace'
]
ARCHITECTURAL_ELEMENTS = [
'IfcWall', 'IfcSlab', 'IfcColumn', 'IfcBeam',
'IfcDoor', 'IfcWindow', 'IfcRoof', 'IfcStair',
'IfcRailing', 'IfcCurtainWall'
]
MEP_ELEMENTS = [
'IfcDuctSegment', 'IfcPipeSegment',
'IfcCableCarrierSegment', 'IfcFlowTerminal'
]
def __init__(self, ifc_path: str):
self.model = ifcopenshell.open(ifc_path)
self.settings = ifcopenshell.geom.settings()
self.settings.set(self.settings.USE_WORLD_COORDS, True)
self.settings.set(self.settings.WELD_VERTICES, True)
def extract_all_elements(self) -> List[BIMElement]:
"""Estrae tutti gli elementi con proprietà e geometria."""
elements = []
all_types = (
self.ARCHITECTURAL_ELEMENTS +
self.MEP_ELEMENTS +
self.SPATIAL_ELEMENTS
)
for ifc_type in all_types:
for entity in self.model.by_type(ifc_type):
element = self._parse_entity(entity)
if element:
elements.append(element)
return elements
def _parse_entity(self, entity) -> Optional[BIMElement]:
"""Converte un'entità IFC in BIMElement."""
try:
# Proprietà di base
props = self._get_properties(entity)
# Geometria 3D
geometry = None
try:
shape = ifcopenshell.geom.create_shape(self.settings, entity)
geometry = self._extract_geometry(shape)
except Exception:
pass # Alcuni elementi non hanno geometria
# Piano di appartenenza
level = self._get_storey(entity)
return BIMElement(
global_id=entity.GlobalId,
ifc_type=entity.is_a(),
name=entity.Name or '',
properties=props,
geometry=geometry,
level=level,
classification=self._get_classification(entity),
)
except Exception as e:
return None
def _get_properties(self, entity) -> dict:
"""Estrae tutti i PropertySet dell'entità."""
props = {}
for definition in entity.IsDefinedBy:
if definition.is_a('IfcRelDefinesByProperties'):
prop_set = definition.RelatingPropertyDefinition
if prop_set.is_a('IfcPropertySet'):
for prop in prop_set.HasProperties:
if prop.is_a('IfcPropertySingleValue') and prop.NominalValue:
props[prop.Name] = prop.NominalValue.wrappedValue
return props
def _extract_geometry(self, shape) -> dict:
"""Converte la geometria IFC in formato Three.js-compatibile."""
geometry = shape.geometry
# Vertici
verts = np.array(geometry.verts).reshape(-1, 3)
# Facce triangolari
faces = np.array(geometry.faces).reshape(-1, 3)
# Normali
normals = np.array(geometry.normals).reshape(-1, 3)
return {
'vertices': verts.tolist(),
'faces': faces.tolist(),
'normals': normals.tolist(),
'vertex_count': len(verts),
'face_count': len(faces),
}
def _get_storey(self, entity) -> Optional[str]:
"""Determina il piano di appartenenza dell'elemento."""
for rel in entity.ContainedInStructure:
container = rel.RelatingStructure
if container.is_a('IfcBuildingStorey'):
return container.Name
return None
def _get_classification(self, entity) -> Optional[str]:
"""Estrae la classificazione (OmniClass, Uniclass, ecc.)."""
for rel in entity.HasAssociations:
if rel.is_a('IfcRelAssociatesClassification'):
ref = rel.RelatingClassification
return f"{ref.ReferencedSource.Name}:{ref.Identification}"
return None
def get_spatial_hierarchy(self) -> dict:
"""
Costruisce la gerarchia spaziale: Building > Storey > Space > Elements
"""
hierarchy = {}
buildings = self.model.by_type('IfcBuilding')
for building in buildings:
b_data = {
'id': building.GlobalId,
'name': building.Name,
'storeys': {},
}
for rel in building.IsDecomposedBy:
for storey in rel.RelatedObjects:
if storey.is_a('IfcBuildingStorey'):
elevation = storey.Elevation or 0
b_data['storeys'][storey.GlobalId] = {
'name': storey.Name,
'elevation': elevation,
'elements': self._get_storey_elements(storey),
}
hierarchy[building.GlobalId] = b_data
return hierarchy
Three.js を使用した BIM Web ビューアー
Web BIM ビューアは、60fps を維持しながら、数百万のポリゴンを持つモデルを処理する必要があります。 ブラウザで。主な技術には、錐台カリング、LOD (詳細レベル)、 繰り返し要素とプログレッシブ読み込みのためのインスタンス化されたレンダリング。
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
import { MeshBVH, acceleratedRaycast } from 'three-mesh-bvh';
// Abilita raycast accelerato per selezione elementi
THREE.Mesh.prototype.raycast = acceleratedRaycast;
class BIMViewer {
private scene: THREE.Scene;
private camera: THREE.PerspectiveCamera;
private renderer: THREE.WebGLRenderer;
private controls: OrbitControls;
private elementMap: Map<string, THREE.Mesh> = new Map();
private selectedElement: THREE.Mesh | null = null;
// Materiali
private defaultMaterial = new THREE.MeshLambertMaterial({
color: 0xcccccc,
transparent: true,
opacity: 0.85,
});
private selectedMaterial = new THREE.MeshLambertMaterial({
color: 0x007bff,
transparent: true,
opacity: 0.9,
});
constructor(container: HTMLElement) {
// Scene setup
this.scene = new THREE.Scene();
this.scene.background = new THREE.Color(0xf0f0f0);
// Camera
this.camera = new THREE.PerspectiveCamera(
45,
container.clientWidth / container.clientHeight,
0.1,
10000
);
this.camera.position.set(50, 30, 50);
// Renderer con antialiasing e shadow map
this.renderer = new THREE.WebGLRenderer({
antialias: true,
powerPreference: 'high-performance',
});
this.renderer.setPixelRatio(window.devicePixelRatio);
this.renderer.setSize(container.clientWidth, container.clientHeight);
this.renderer.shadowMap.enabled = true;
this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;
container.appendChild(this.renderer.domElement);
// Luci
const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
const dirLight = new THREE.DirectionalLight(0xffffff, 0.8);
dirLight.position.set(100, 100, 50);
dirLight.castShadow = true;
this.scene.add(ambientLight, dirLight);
// Controls
this.controls = new OrbitControls(this.camera, this.renderer.domElement);
this.controls.enableDamping = true;
this.controls.dampingFactor = 0.05;
// Event listeners
this.renderer.domElement.addEventListener('click', this.onElementClick.bind(this));
window.addEventListener('resize', this.onWindowResize.bind(this));
}
loadModel(elements: BIMElementGeometry[]) {
/**
* Carica il modello BIM ottimizzando per performance.
* Usa InstanedMesh per elementi ripetuti (porte, finestre).
*/
const elementsByType = this.groupByType(elements);
for (const [type, typeElements] of Object.entries(elementsByType)) {
if (typeElements.length > 10) {
// Instanced rendering per elementi ripetuti
this.addInstancedElements(type, typeElements);
} else {
// Mesh singole per elementi unici
typeElements.forEach(elem => this.addSingleElement(elem));
}
}
// Centra la camera sul modello
this.fitCameraToModel();
this.animate();
}
private addSingleElement(element: BIMElementGeometry) {
const geometry = new THREE.BufferGeometry();
geometry.setAttribute(
'position',
new THREE.Float32BufferAttribute(element.vertices.flat(), 3)
);
geometry.setAttribute(
'normal',
new THREE.Float32BufferAttribute(element.normals.flat(), 3)
);
geometry.setIndex(element.faces.flat());
// Ottimizzazione: BVH per raycast veloce
geometry.computeBoundsTree = MeshBVH.bind(null, geometry);
geometry.computeBoundsTree();
const material = this.getMaterialForType(element.ifcType);
const mesh = new THREE.Mesh(geometry, material.clone());
mesh.userData = {
globalId: element.globalId,
ifcType: element.ifcType,
name: element.name,
properties: element.properties,
};
mesh.castShadow = true;
mesh.receiveShadow = true;
this.elementMap.set(element.globalId, mesh);
this.scene.add(mesh);
}
private onElementClick(event: MouseEvent) {
const rect = this.renderer.domElement.getBoundingClientRect();
const mouse = new THREE.Vector2(
((event.clientX - rect.left) / rect.width) * 2 - 1,
-((event.clientY - rect.top) / rect.height) * 2 + 1
);
const raycaster = new THREE.Raycaster();
raycaster.setFromCamera(mouse, this.camera);
const meshes = Array.from(this.elementMap.values());
const intersects = raycaster.intersectObjects(meshes);
if (intersects.length > 0) {
const clicked = intersects[0].object as THREE.Mesh;
this.selectElement(clicked.userData['globalId']);
}
}
selectElement(globalId: string) {
// Ripristina il materiale dell'elemento precedente
if (this.selectedElement) {
const type = this.selectedElement.userData['ifcType'];
(this.selectedElement.material as THREE.MeshLambertMaterial)
.copy(this.getMaterialForType(type));
}
const mesh = this.elementMap.get(globalId);
if (mesh) {
(mesh.material as THREE.MeshLambertMaterial).copy(this.selectedMaterial);
this.selectedElement = mesh;
// Emit evento con proprietà dell'elemento
this.onElementSelected(mesh.userData);
}
}
private animate() {
requestAnimationFrame(this.animate.bind(this));
this.controls.update();
this.renderer.render(this.scene, this.camera);
}
}
衝突検出: 3D 衝突検出
干渉検出により、BIM 要素間の干渉が自動的に特定されます。 さまざまな分野 (例: 構造梁と交差するチューブ)。それは 1 つです 設計段階での最も重要な BIM ユースケースを紹介します。
import ifcopenshell
import ifcopenshell.geom
import numpy as np
from scipy.spatial import KDTree
from typing import List, Tuple
from dataclasses import dataclass
@dataclass
class Clash:
element1_id: str
element1_type: str
element2_id: str
element2_type: str
clash_type: str # 'hard' | 'soft' | 'clearance'
distance: float
location: Tuple[float, float, float]
severity: str # 'critical' | 'major' | 'minor'
class ClashDetector:
"""
Rilevamento automatico di conflitti geometrici tra elementi BIM.
Supporta Hard Clash, Soft Clash e Clearance Clash.
"""
DISCIPLINE_PAIRS = [
('IfcBeam', 'IfcDuctSegment'),
('IfcBeam', 'IfcPipeSegment'),
('IfcColumn', 'IfcDuctSegment'),
('IfcSlab', 'IfcPipeSegment'),
('IfcWall', 'IfcDuctSegment'),
]
# Tolleranza minima per soft clash (mm convertiti in m)
CLEARANCE_TOLERANCE = {
'IfcDuctSegment': 0.10, # 100mm di spazio attorno ai condotti
'IfcPipeSegment': 0.05, # 50mm attorno ai tubi
'default': 0.025, # 25mm default
}
def __init__(self, ifc_model):
self.model = ifc_model
self.settings = ifcopenshell.geom.settings()
self.settings.set(self.settings.USE_WORLD_COORDS, True)
def detect_clashes(self, disciplines: List[str] = None) -> List[Clash]:
"""
Rileva tutti i clash tra le discipline specificate.
"""
clashes = []
pairs = disciplines or self.DISCIPLINE_PAIRS
for type_a, type_b in pairs:
elements_a = self._get_element_aabbs(type_a)
elements_b = self._get_element_aabbs(type_b)
if not elements_a or not elements_b:
continue
# Accelera la ricerca con KDTree
centers_b = np.array([e['center'] for e in elements_b])
tree = KDTree(centers_b)
for elem_a in elements_a:
# Cerca elementi vicini (raggio approssimativo)
radius = np.linalg.norm(elem_a['extents']) + max(
np.linalg.norm(e['extents']) for e in elements_b
)
nearby_indices = tree.query_ball_point(elem_a['center'], radius)
for idx in nearby_indices:
elem_b = elements_b[idx]
clash = self._check_aabb_clash(elem_a, elem_b, type_b)
if clash:
clashes.append(clash)
return clashes
def _get_element_aabbs(self, ifc_type: str) -> List[dict]:
"""Calcola AABB (Axis-Aligned Bounding Box) per ogni elemento."""
aabbs = []
for entity in self.model.by_type(ifc_type):
try:
shape = ifcopenshell.geom.create_shape(self.settings, entity)
verts = np.array(shape.geometry.verts).reshape(-1, 3)
min_pt = verts.min(axis=0)
max_pt = verts.max(axis=0)
center = (min_pt + max_pt) / 2
extents = (max_pt - min_pt) / 2
aabbs.append({
'id': entity.GlobalId,
'type': ifc_type,
'name': entity.Name or '',
'min': min_pt,
'max': max_pt,
'center': center,
'extents': extents,
})
except Exception:
pass
return aabbs
def _check_aabb_clash(self, a: dict, b: dict, type_b: str) -> Optional[Clash]:
"""Controlla se due AABB si intersecano (hard clash) o sono troppo vicine."""
clearance = self.CLEARANCE_TOLERANCE.get(type_b, self.CLEARANCE_TOLERANCE['default'])
# Hard clash: le bounding box si sovrappongono
overlap = all(
a['min'][i] <= b['max'][i] and a['max'][i] >= b['min'][i]
for i in range(3)
)
if overlap:
return Clash(
element1_id=a['id'],
element1_type=a['type'],
element2_id=b['id'],
element2_type=b['type'],
clash_type='hard',
distance=0.0,
location=tuple((a['center'] + b['center']) / 2),
severity='critical',
)
# Clearance clash: troppo vicini
distance = max(
max(a['min'][i] - b['max'][i], b['min'][i] - a['max'][i], 0)
for i in range(3)
)
if distance < clearance:
return Clash(
element1_id=a['id'],
element1_type=a['type'],
element2_id=b['id'],
element2_type=b['type'],
clash_type='clearance',
distance=round(distance * 1000, 1), # mm
location=tuple((a['center'] + b['center']) / 2),
severity='major' if distance < clearance / 2 else 'minor',
)
return None
クラウド BIM アーキテクチャ: マルチユーザー コラボレーション
クラウド BIM システムの主な課題は次のとおりです。 リアルタイムのコラボレーション 重量が数百 MB になる可能性があるモデルの場合。 Autodesk BIM 360 などのエンタープライズ ソリューション、 Trimble Connect と Bentley iTwin は異なるアプローチを採用していますが、それらはすべて共通のパターンを持っています。 モデルフェデレーション, 増分変更セット e 競合の解決.
from dataclasses import dataclass, field
from typing import List, Dict, Optional
from datetime import datetime
import hashlib
import json
@dataclass
class BIMChangeSet:
"""Rappresenta un set di modifiche atomiche al modello BIM."""
id: str
model_id: str
author_id: str
timestamp: datetime
operations: List[dict] # Lista di operazioni CRUD sugli elementi
parent_revision: str
description: str
discipline: str # 'architectural' | 'structural' | 'mep'
class BIMCollaborationService:
"""
Gestione della collaborazione multi-disciplina su modelli BIM.
Implementa Operational Transformation per merge dei changeset.
"""
def __init__(self, storage, event_bus):
self.storage = storage
self.event_bus = event_bus
async def apply_changeset(self, changeset: BIMChangeSet) -> dict:
"""
Applica un changeset al modello con conflict detection.
"""
# Carica revisione parent
parent = await self.storage.get_revision(changeset.parent_revision)
# Verifica conflitti con changeset paralleli
concurrent_changes = await self.storage.get_concurrent_changesets(
model_id=changeset.model_id,
since=parent.timestamp,
exclude_id=changeset.id,
)
conflicts = self._detect_conflicts(changeset, concurrent_changes)
if conflicts and not self._auto_resolvable(conflicts):
return {
'status': 'conflict',
'conflicts': conflicts,
'message': 'Risoluzione manuale richiesta',
}
# Applica le operazioni
new_revision_id = await self.storage.apply_operations(
model_id=changeset.model_id,
operations=changeset.operations,
parent_revision=changeset.parent_revision,
)
# Notifica tutti i collaboratori
await self.event_bus.publish('model.updated', {
'model_id': changeset.model_id,
'revision_id': new_revision_id,
'author': changeset.author_id,
'discipline': changeset.discipline,
'change_count': len(changeset.operations),
})
return {
'status': 'success',
'revision_id': new_revision_id,
'applied_operations': len(changeset.operations),
}
def _detect_conflicts(self, changeset: BIMChangeSet,
concurrent: List[BIMChangeSet]) -> List[dict]:
"""Identifica elementi modificati concorrentemente."""
changed_ids = {op['element_id'] for op in changeset.operations}
conflicts = []
for concurrent_cs in concurrent:
concurrent_ids = {op['element_id'] for op in concurrent_cs.operations}
overlap = changed_ids.intersection(concurrent_ids)
for element_id in overlap:
conflicts.append({
'element_id': element_id,
'conflict_with': concurrent_cs.id,
'author': concurrent_cs.author_id,
'discipline': concurrent_cs.discipline,
})
return conflicts
def _auto_resolvable(self, conflicts: List[dict]) -> bool:
"""
Determina se i conflitti possono essere risolti automaticamente.
Regola: modifiche a discipline diverse sullo stesso elemento
possono essere mergate se riguardano attributi diversi.
"""
# Implementazione semplificata: conflitti tra discipline diverse
# sono auto-resolvibili se gli attributi modificati non si sovrappongono
disciplines_involved = {c['discipline'] for c in conflicts}
return len(disciplines_involved) > 1 # Discipline diverse = auto-merge
デジタルツインのためのBIM-IoT統合
静的 BIM への次のステップは、 デジタルツイン 動的: 建物に設置されたセンサーからのリアルタイム IoT データで強化された BIM モデル (温度、占有率、空気の質、エネルギー消費)。
BIM デジタル ツインのコンポーネント
- BIM モデル レイヤー: 静的プロパティを持つ 3D IFC ジオメトリ
- IoT センサー層: GlobalId を介して IFC 要素にマッピングされた物理センサー
- 時系列レイヤー: 過去のセンサー データ用の InfluxDB または TimescaleDB
- 分析レイヤー: 消費予測、異常検出、占有予測のための ML
- 視覚化レイヤー: リアルタイム IoT データ オーバーレイを備えた 3D BIM ビューア
from influxdb_client import InfluxDBClient, Point
from influxdb_client.client.write_api import SYNCHRONOUS
from datetime import datetime
import asyncio
class BIMDigitalTwin:
"""
Integrazione BIM + IoT per Digital Twin degli edifici.
Mappa sensori fisici agli elementi IFC e persiste i dati in InfluxDB.
"""
def __init__(self, ifc_model, influx_client: InfluxDBClient,
sensor_mapping: dict):
"""
sensor_mapping: { 'sensor_id': 'ifc_global_id' }
Mappa ogni sensore fisico all'elemento IFC corrispondente.
"""
self.model = ifc_model
self.influx = influx_client
self.write_api = influx_client.write_api(write_options=SYNCHRONOUS)
self.query_api = influx_client.query_api()
self.sensor_mapping = sensor_mapping
async def ingest_sensor_reading(self, sensor_id: str,
measurement_type: str,
value: float,
timestamp: datetime):
"""
Persiste una lettura del sensore associandola all'elemento IFC.
"""
ifc_element_id = self.sensor_mapping.get(sensor_id)
if not ifc_element_id:
return
# Recupera metadati elemento IFC
element = self.model.by_guid(ifc_element_id)
storey = self._get_storey(element)
point = (
Point(measurement_type)
.tag("sensor_id", sensor_id)
.tag("ifc_element_id", ifc_element_id)
.tag("ifc_type", element.is_a())
.tag("storey", storey or "unknown")
.field("value", value)
.time(timestamp)
)
self.write_api.write(bucket="bim_sensors", record=point)
async def get_element_history(self, ifc_element_id: str,
measurement: str,
hours: int = 24) -> list:
"""Recupera la storia delle letture per un elemento IFC."""
query = f'''
from(bucket: "bim_sensors")
|> range(start: -{hours}h)
|> filter(fn: (r) => r["ifc_element_id"] == "{ifc_element_id}")
|> filter(fn: (r) => r["_measurement"] == "{measurement}")
|> aggregateWindow(every: 5m, fn: mean)
'''
tables = self.query_api.query(query)
readings = []
for table in tables:
for record in table.records:
readings.append({
'timestamp': record.get_time().isoformat(),
'value': record.get_value(),
})
return readings
async def detect_anomalies(self, storey: str,
measurement: str) -> list:
"""
Rileva anomalie nelle letture dei sensori usando z-score.
"""
query = f'''
from(bucket: "bim_sensors")
|> range(start: -7d)
|> filter(fn: (r) => r["storey"] == "{storey}")
|> filter(fn: (r) => r["_measurement"] == "{measurement}")
'''
# Implementa anomaly detection basica su z-score...
return []
BIM クラウドの技術的課題
- 大きなファイル: 複雑な BIM モデルは 500MB を超える場合があります。 HTTP Range リクエストと要素のプログレッシブ読み込みでストリーミングを使用します。
- 規律の衝突: 建築、構造、MEP は、同じ空間を変更することがよくあります。ストアレベルのロックとプッシュ通知を実装します。
- バージョン管理: BIM モデルは、ソース コードのようなテキストの差分には適していません。チェンジセットとオブジェクトレベルのバージョン管理アプローチを採用します。
- 相互運用性: すべてのソフトウェアが IFC を正しくエクスポートできるわけではありません。インポート前の検証と正規化には ifcopenshell を使用します。
結論と次のステップ
最新の BIM アーキテクチャは、3D 解析を統合した複雑な分散システムです。 リアルタイム コラボレーション、クラウド レンダリング、IoT。 IFC 4.3 標準 (ISO 16739-1:2024) ifcopenshell や Three.js などのライブラリはツール間の相互運用性を保証します。 これらを使用すると、カスタム ビューアとパイプラインを構築できます。デジタルツインへの移行、 IoT と ML の統合により、実際の建物に向けた BIM の自然な進化です 賢い。
PropTech シリーズの他の記事を探す
- 記事 03 - スマート ビルディング IoT: センサー統合とエッジ コンピューティング
- 記事 08 - バーチャル不動産ツアー: WebGL と 3D Web テクノロジー
- 第 07 条 - PostGIS を使用した地理空間検索および位置サービス







