Automating Robot Structural Analysis with Python and AI
A field report: from the formwork plan to an analyzed Robot model and DXF reinforcement drawings, using a Python script written by AI. Method, API code, pitfalls, and checks.
- Category
- STRAI & Structures
- Published
- Reading
- 11 min
- Language
- EN

Contents
Building a model in Robot Structural Analysis node by node and bar by bar, then drawing the reinforcement for every column and every beam: on a single house, that adds up to days of repetitive work. Yet Robot has an application programming interface (API) that lets you drive the whole program from Python.
I’m not a programmer, so I asked Claude to write the script for me, then tested it on two real reinforced concrete building projects. This article covers the method, the code that actually works, and above all what doesn’t.
Key point before you start: this script produces a model and drawings that still need checking. Choosing the structural system, setting the assumptions, and validating the results remain the engineer’s job. No result should be signed off without the checks described below.
The principle: AI writes, the engineer runs and validates
The AI doesn’t drive Robot directly. The roles are split simply:
- Claude reads the plans, writes the Python script, and analyzes the exported results.
- I run the script on my computer, where Robot is installed.
- The script builds the model, runs the analysis, and exports the internal forces to CSV files.
- Claude reads those CSV files, and the script produces the reinforcement drawings as DXF.
- I check every step before moving on to the next.
The setup:
| Item | Version |
|---|---|
| Analysis software | Autodesk Robot Structural Analysis Professional 2014, French version |
| Language | Python 3.13, IDLE editor |
| Connection to Robot | pywin32 library (pip install pywin32) |
| Reading DXF plans | AutoCAD |
The API commands quoted in this article come from a script used on Robot 2014 for two real projects. The code snippets themselves are simplified for readability and have not been run as they appear here. The COM API still exists in recent versions of Robot, but some behaviors may differ.
The 7-step workflow
- Inputs: formwork plan and architectural plan (DXF or PDF): grid lines, columns, beams, levels.
- Plan review and validation by the engineer: grid lines, sections, floor slabs, loads, soil and site data.
- Project data entry in the script: grid line coordinates, list of columns and beams, floor slab type.
- Modeling and analysis in Robot: nodes, bars, supports, loads, seismic action, combinations.
- Equilibrium check: if the difference between the computed loads and the reactions is above the threshold, go back to step 3 and fix the inputs.
- Reinforcement, floor slabs, and foundations, from the exported internal forces. If a column has no reasonable solution, its section is enlarged and the full analysis is rerun.
- Review of the results before sign-off: CSV files, DXF drawings opened in AutoCAD, calculation log.
The key rule: no reinforcement is calculated until the model is in equilibrium. That rule keeps you from drawing plans from a wrong model.
One script, clearly separated sections
On the first project, the work was spread over several scripts as I learned. On the second, everything fits in a single file of about 880 lines that runs in roughly fifty seconds.
The file is organized so that a new project only requires changes to the first three sections:
| Section | Contents | Edit for each project? |
|---|---|---|
| 1. Project | Project name, starting empty template model, steps to run | Yes |
| 2. Geometry | Grid lines, columns, sections, levels, list of beams | Yes, from the formwork plan |
| 3. Loads, seismic, soil | Finishes, floor slabs, parapets, seismic parameters, allowable soil pressure | Yes |
| 4 to 10 | Floor slab design, Robot control, reinforcement, foundations, DXF drawing | No |
This separation is what makes the time savings real: the “engine” doesn’t get rewritten for every project.
Connecting to Robot from Python
Here are the basic API commands. One detail to remember from the start: the API always works in newtons and meters, whatever units are displayed in Robot.
The API commands are taken verbatim from a script used on Robot 2014 for two real projects. The code around them is simplified for readability and has not been run as it appears here: the snippets follow on from one another, and the example data (four columns, four beams) should be replaced with your project’s. Before using them on a project, try them on a small model (a single column) and compare the support reactions with a hand calculation.
Connecting, opening, and saving
import osimport win32com.clientfrom win32com.client import constants as C
DOSSIER = os.path.dirname(os.path.abspath(__file__))PROJET = "mon_projet"MODELE_VIDE = "MODEL.VIDE.rtd" # empty template model prepared by hand: sections, materials, display units
robot = win32com.client.gencache.EnsureDispatch("Robot.Application")robot.Visible = 1projet = robot.Project
# Open the empty template model, then save it under the project namechemin = os.path.join(DOSSIER, MODELE_VIDE)projet.Open(chemin)projet.SaveAs(os.path.join(DOSSIER, PROJET + ".rtd"))st = projet.StructureNodes, bars, and sections
# Example data, same structure as section 2 of the script (m)AXES_X = {"A": 0.00, "B": 5.65}AXES_Y = {1: 0.00, 2: 3.90}POTEAUX = {"P1": ("A", 1), "P2": ("B", 1), "P3": ("A", 2), "P4": ("B", 2)}SECTION_POTEAU = {"defaut": "C30x30"}Z_ENCAST = -0.45H = 3.80POUTRES = [("A1", "P1", "P2", "B25x40", H), ("A2", "P3", "P4", "B25x40", H), ("A3", "P1", "P3", "B25x40", H), ("A4", "P2", "P4", "B25x40", H)]
# Derived geometry (copied from the script)YMAX = max(AXES_Y.values())XY = {p: (AXES_X[a], YMAX - AXES_Y[r]) for p, (a, r) in POTEAUX.items()}POUTRE = {n: dict(nom=n, a=a, b=b, sec=s, z=z) for n, a, b, s, z in POUTRES}
def niveaux_poteau(p): zs = sorted({q["z"] for q in POUTRE.values() if p in (q["a"], q["b"])}) return zs or [H]
def sec_poteau(p): return SECTION_POTEAU.get(p, SECTION_POTEAU["defaut"])
# Nodes.Create(number, x, y, z) in m; Bars.Create(number, start node, end node)nid, n = {}, 0for p in POTEAUX: for z in [Z_ENCAST] + niveaux_poteau(p): n += 1; st.Nodes.Create(n, XY[p][0], XY[p][1], z); nid[(p, z)] = nbarres_pot, barres_pou, b = {}, {}, 0for p in POTEAUX: zs = [Z_ENCAST] + niveaux_poteau(p) for z0, z1 in zip(zs[:-1], zs[1:]): b += 1; st.Bars.Create(b, nid[(p, z0)], nid[(p, z1)]); barres_pot[b] = (p, z0, z1)for q in POUTRE.values(): b += 1; st.Bars.Create(b, nid[(q["a"], q["z"])], nid[(q["b"], q["z"])]); barres_pou[b] = q["nom"]
# The sections must already exist in the empty template model (see the pitfalls)for bb, (p, z0, z1) in barres_pot.items(): st.Bars.Get(bb).SetLabel(C.I_LT_BAR_SECTION, sec_poteau(p))for bb, nom in barres_pou.items(): st.Bars.Get(bb).SetLabel(C.I_LT_BAR_SECTION, POUTRE[nom]["sec"])Fixed supports
lab = st.Labels.Create(C.I_LT_SUPPORT, "ENCAST")d = lab.Data; d.UX = 1; d.UY = 1; d.UZ = 1; d.RX = 1; d.RY = 1; d.RZ = 1st.Labels.Store(lab)for p in POTEAUX: st.Nodes.Get(nid[(p, Z_ENCAST)]).SetLabel(C.I_LT_SUPPORT, "ENCAST")Load cases and combinations
COMBI_ELU, COMBI_ELS = 3, 4
# Dead load case with self-weight; live load case: the constant really is spelled "EXPLOATATION"cases = st.CasesG = cases.CreateSimple(1, "G", C.I_CN_PERMANENT, C.I_CAT_STATIC_LINEAR)Q = cases.CreateSimple(2, "Q", C.I_CN_EXPLOATATION, C.I_CAT_STATIC_LINEAR)r = G.Records.Get(G.Records.New(C.I_LRT_DEAD))r.SetValue(C.I_DRV_Z, -1); r.SetValue(C.I_DRV_ENTIRE_STRUCTURE, True)
# Uniform load on a beam: value in kN/m, converted to N/mdef uni(cas, bb, kN): if abs(kN) < 1e-6: return rr = cas.Records.Get(cas.Records.New(C.I_LRT_BAR_UNIFORM)) rr.SetValue(C.I_BURV_PZ, -1000.0 * kN); rr.Objects.FromText(str(bb))
# Example: 12 kN/m dead load on every beam (the script computes these loads from the floor slabs)for bb, nom in barres_pou.items(): uni(G, bb, 12.0) # 12 kN/m: uni() multiplies by 1000, so the API receives -12000 N/m
# ULS and SLS combinations (ELU and ELS in French)cat = getattr(C, "I_CAT_COMB", None)if cat is None: cat = C.I_CAT_COMB_LINEARelu = cases.CreateCombination(COMBI_ELU, "ELU", C.I_CBT_ULS, C.I_CN_PERMANENT, cat)elu.CaseFactors.New(1, 1.35); elu.CaseFactors.New(2, 1.5)els = cases.CreateCombination(COMBI_ELS, "ELS", C.I_CBT_SLS, C.I_CN_PERMANENT, cat)els.CaseFactors.New(1, 1.0); els.CaseFactors.New(2, 1.0)Running the analysis and reading the results
projet.CalcEngine.Calculate()res = st.Resultscombis = [COMBI_ELU, COMBI_ELS]
# Internal forces: bar, combination, position along the bar (0 to 1); the API returns N and N·mfor bb, nom in barres_pou.items(): for c in combis: for pos in (0.0, 0.25, 0.5, 0.75, 1.0): F = res.Bars.Forces.Value(bb, c, pos) print(nom, c, pos, round(F.FZ / 1000, 2), round(F.MY / 1000, 2)) # kN, kN·m
# Reactions: node, case; the API returns Nfor p in POTEAUX: for c in combis: R = res.Nodes.Reactions.Value(nid[(p, Z_ENCAST)], c) print(p, c, round(R.FZ / 1000, 2)) # kNWhat doesn’t work (in Robot 2014)
This is the part you’ll hardly find anywhere else. Several API functions seem to work but leave you with an unusable model:
- Creating a concrete section in code: the section name is created, but the analysis returns « Section non définie » (Section not defined). The fix: create all sections by hand in the empty template model, once and for all.
Labels.Exist: reports the label as missing even when the section exists. Don’t use it.- Cladding in code: the outline is created, but without the cladding property, and the area load never reaches the beams. Cladding created by hand works. So in the script, floor slab loads are computed in Python and applied directly to the beams.
- The actual reinforcement design module: it can’t be automated. That’s why the script calculates the reinforcement itself, from the internal forces exported from Robot.
The pitfalls that waste the most time
Looking back, most of the lost time didn’t come from the analysis, but from these details:
| Symptom | Cause | Fix |
|---|---|---|
| The result is identical to the previous version of the script | The script window was still open in IDLE and wrote the old code back over the new one | Close the window without saving, reopen it, and check a line that only exists in the new version |
PermissionError | A CSV or DXF file is open in Excel or AutoCAD | Close the files before running the script |
| « Section non définie pour la barre n° … » (Section not defined for bar no. …) | The section is missing from the empty template model, or its name differs (an extra space) | Add the section by hand in the empty template model |
| « Le projet ouvert n’est pas vide » (The open project is not empty) | The empty template model was saved with a model inside it | Recreate the empty template model, and answer « Non » (No) when Robot offers to save it |
| All results are zero | The analysis failed without Robot reporting it to Python | Open the calculation messages window in Robot |
| A soil pressure of 20 MPa | Unit mix-up: 2 bar = 0.20 MPa | Any soil pressure in the tens of MPa is an error |
I made that last mistake myself, twice. The AI caught it because I had asked it to flag any inconsistent value.
Mandatory checks
The script prints most of these checks automatically. No result is used until all of them pass:
- Vertical equilibrium: under G, the script compares the sum of the reactions in Robot with the load computed in Python. If the difference exceeds 5%, it stops before the reinforcement step. Under Q, both values are only printed, and the comparison is done by eye.
- Seismic base shear: the sum of the horizontal reactions must equal the computed seismic force, in all four directions.
- Displacements: story drifts must stay within the limits of the seismic code.
- Hand check of at least one element: for example, the midspan moment of a beam recalculated by hand.
- Consistency between the model and the drawings: if a column was enlarged for the reinforcement, it must be enlarged in the model too, and the analysis rerun, because stiffness changes how seismic forces are distributed.
- Reading every warning from the script: columns with no reasonable solution, excessive floor slab spans, large deflections.
A model in equilibrium is not necessarily a correct model
The first two checks have a limit you need to know about: they compare Python and Robot, which both work from the same geometry. If a column was placed on the wrong grid line, or a span was copied wrong from the plan, the model stays perfectly in equilibrium, and still wrong.
So the real risk in this workflow is data entry, not the analysis. Two independent checks cover it:
- Geometry overlay: export the model geometry (grid lines, columns, beams with their names) to DXF, then overlay it on the original formwork plan in AutoCAD. Any mismatch shows up in seconds.
- A hand load takedown on one column, compared with the axial force from Robot.
The seismic method has conditions of use
The script applies the equivalent static method of the seismic code. That method is only valid if the building meets the regularity conditions, in plan and in elevation. A building with split levels, for example, needs an explicit check before using it. Otherwise, a response spectrum (modal) analysis is required. That’s an engineering decision, to be made and recorded for each project.
Finally, the DXF drawings produced are engineering drafts: I rework them in AutoCAD before issuing anything.
A job-site rule in the code
On the sites where I work, beams and columns rarely use bars larger than HA14 (14 mm high-bond bars). So I gave the script two rules: no over-reinforcement, and no bar diameter above HA14. If an element doesn’t pass within that limit, the script never drops the steel below the required minimum: it flags the element and suggests a solution, such as a larger section or two layers of bars.
It’s a good example of what the engineer brings: the AI applies the code, but it doesn’t know site practice unless you tell it.
The results
- Time saved: reinforcement detailing used to take me a full day; it now takes about two hours, checks included.
- What stays manual: creating the sections in the empty template model, cladding, the actual reinforcement design module, and the final layout of the drawings.
- What doesn’t change: responsibility. The script speeds up production, but every assumption and every result is still validated by the engineer.
Going further
Several improvements are still on the list: automatic geometry export for the overlay with the formwork plan, a rigid diaphragm at each level, response spectrum analysis directly in Robot, elastic supports, stairs and shear walls, and multistory buildings. I’ll come back to these in future articles.
If you want to try this, start small: a single-story frame, one support, one load, and compare the reactions with a hand calculation. It’s the best way to understand the API before trusting it with a real project.
This article was written with the help of AI tools, then reviewed and checked.