acados에서 QP solver failed가 나왔다고 해서 바로 “문제에 해가 없다”고 결론 내릴 수는 없습니다. 바깥쪽 비선형 최적화의 종료 상태와, 그 과정에서 풀던 이차계획 문제의 상태는 다른 정보입니다. 종료 코드가 가리키는 층위를 확인한 뒤 잔차와 초기값을 함께 읽어야 원인을 좁힐 수 있습니다.
예측 제어를 구현하는 독자라면 MPC의 역할과 제약에서 한 단계 더 내려온 문제입니다. 제어 목표를 잘 세웠는지와, 그 목표를 표현한 수치 문제가 이번 호출에서 어떻게 풀렸는지를 나눠 보겠습니다.
바깥 문제의 종료 이유와 안쪽 문제의 상태
NLP는 비선형계획 문제를 뜻합니다. SQP 계열 방법은 현재 추정값 주변에서 이차계획 문제인 QP를 구성하고 풀어, 다음 추정값으로 갈 방향을 구합니다. 따라서 전체 반복의 종료 이유와 개별 QP 풀이의 상태가 함께 존재합니다.
acados 문제 해결 문서는 먼저 solver status를 확인하고, NLP 잔차를 본 뒤 QP backend의 상태 정의를 확인하라고 안내합니다. HPIPM을 쓴다면 그 backend의 상태 코드를 읽어야 한다는 뜻입니다. 같은 정수를 보았다는 이유로 서로 다른 솔버의 코드표를 교환해서 쓸 수는 없습니다.
현재 Python API의 AcadosOcpSolver.status는 마지막 호출의 종료 상태를 나타냅니다. 예를 들어 1은 NaN 검출, 2는 최대 반복 횟수 도달, 4는 QP 풀이 실패, 7은 시간 초과로 설명되어 있습니다. 이 가운데 2를 “QP가 불가능함”으로 읽거나, 4를 “실제 로봇의 목표가 물리적으로 불가능함”으로 읽으면 서로 다른 판단을 섞게 됩니다.
QP 쪽 기록은 같은 API의 get_stats에서 따로 설명합니다. qp_stat은 마지막 NLP 호출에 포함된 QP 상태들의 벡터이고, qp_iter는 그 QP들의 반복 횟수입니다. 전체 호출의 상태와 이 벡터를 연결해야 어느 단계에서 풀이가 막혔는지 볼 수 있습니다.
문서 표기에도 주의할 점이 있습니다. 같은 날 확인한 짧은 문제 해결 페이지는 Python의 get_status()를 언급하지만, 상세 API 페이지는 status 속성을 문서화합니다. 이 글은 solve()의 반환 상태와 상세 API의 status 설명을 기준으로 읽습니다. 이 차이만으로 특정 설치 버전에서 어느 이름이 동작한다고 단정하지는 않습니다.
잔차 네 개는 같은 종류의 오차가 아니다
종료 상태 다음에는 현재 추정값이 무엇을 아직 만족하지 못했는지 봅니다. 공식 get_residuals 설명은 다음 순서의 값을 구분합니다.
| 반환 항목 | 확인하는 조건 |
|---|---|
res_stat |
최적성의 정지 조건 |
res_eq |
동역학 등을 포함한 등식 제약 |
res_ineq |
부등식 제약 |
res_comp |
제약 여유와 대응 승수의 상보 조건 |
예를 들어 반복 한도에 도달한 결과라도 동역학 등식 오차가 남은 경우와, 등식은 충분히 맞지만 최적성 조건이 덜 충족된 경우는 후속 조사가 달라집니다. “최대 반복 수를 늘린다”는 같은 조치가 두 경우의 원인을 모두 설명하지는 못합니다. 비용과 제약의 스케일, 모델 함수와 도함수, 초기 추정값을 해당 잔차와 연결해 보아야 합니다.
네 값은 서로 다른 조건을 나타내므로 숫자가 가장 큰 항목을 곧바로 유일한 원인이라고 볼 수도 없습니다. 각 잔차를 문제의 단위·스케일과 해당 종료 허용오차에 맞춰 비교해야 합니다.
또한 NLP 잔차와 마지막 QP의 잔차를 같은 것으로 취급하면 안 됩니다. 상세 API는 qp_residuals를 마지막 QP 풀이의 잔차로 따로 정의하고, nlp_solver_ext_qp_res가 켜져 있고 NLP solver가 SQP일 때 제공되는 조건을 명시합니다. 로그에 필드가 없다고 잔차가 0이었다고 기록할 수는 없습니다.
초기값 오류는 제약 위반보다 앞에서 생길 수 있다
문제 해결 문서가 초기값을 강조하는 이유는, 솔버가 출발점에서 모델 함수와 도함수를 평가해야 하기 때문입니다. 최종적으로 원하는 상태가 타당해도 초기 추정값에서는 식 자체가 정의되지 않을 수 있습니다.
가령 설명용 모델에 실수 범위의 sqrt(x)가 있는데 초기 추정값으로 음수를 넣었다고 하겠습니다. 이 경우에는 “목표 상태에 도달할 수 있는가”를 따지기 전에 함수가 정의되는 영역부터 확인해야 합니다.
초기점에서 함수가 유한한 값을 갖는지와, 그 초기점이 모든 제약을 이미 만족하는지는 서로 다른 질문입니다. 전자를 확인하지 않고 비용 가중치나 반복 한도만 바꾸면, 정의되지 않은 계산을 계속 반복할 수 있습니다. 문서는 set으로 다른 초기화를 제공하는 접근을 안내하지만, 어느 변수와 단계에 어떤 값을 넣을지는 해당 모델의 정의에서 결정해야 합니다.
QP 실패가 계속된다면 Hessian 진단도 별도의 단서를 제공합니다. qp_diagnostics는 FULL_HESSIAN 또는 PROJECTED_HESSIAN을 대상으로 고유값과 조건수 관련 정보를 반환하며, 문서상 대상은 condensing 전의 Hessian입니다. 이 결과를 condensing 뒤 행렬의 직접 측정값처럼 설명해서는 안 됩니다. 어떤 행렬을 진단했는지와 선택한 QP backend의 요구 조건을 함께 읽어야 합니다.
진단 호출이 풀이 상태를 바꾸는 경우
잔차 조회를 추가하는 일이 항상 관찰만 하는 작업은 아닙니다. 공식 문서는 SQP_RTI에서는 잔차가 기본적으로 계산되어 있지 않아 별도 계산이 필요하다고 설명합니다. 그런데 get_residuals(recompute=True)는 최신 문제 데이터에 대해 잔차를 다시 계산하면서 기존 선형화 정보를 덮어쓸 수 있습니다. 문서가 예로 드는 qp_in 값은 AS-RTI가 올바르게 동작하는 데 필요합니다.
따라서 이미 실행 중인 제어 루프의 임의 위치에 이 호출을 끼워 넣고 “로그만 추가했다”고 볼 수 없습니다. 사용하는 RTI 방식과 계산 단계를 확인해야 하며, 진단을 위해 바꾼 호출 순서가 원래 풀이 흐름을 바꾸지 않았는지도 구별해야 합니다.
오류 보고를 읽을 때 가장 유용한 연결은 전체 종료 상태 → 해당 QP 상태 → 남은 조건의 잔차 → 그 지점의 초기값과 모델 정의입니다. 이 연결이 보이면 같은 4라는 종료 코드 뒤에 서로 다른 문제가 있다는 사실을 설명할 수 있습니다. 해를 반환했는지, 수치 조건을 만족했는지, 실제 장치가 의도대로 움직였는지는 여전히 별도로 확인할 결과입니다.
자료 확인: 2026년 10월 15일의 acados 공식 문제 해결 문서와 Python API. 여기서는 문서를 읽어 진단 정보의 의미와 호출 조건을 설명했으며, 솔버 설치·수치 문제 실행·로봇 시험은 수행하지 않았습니다.