TSID에서 URDF 토크 한계가 자동 제약이 아닌 이유

TSID 예제에서 로봇 모델의 토크 한계를 읽고 bounds 객체를 만들었다고 해서, 그 한계가 곧바로 최적화 문제에 들어가는 것은 아닙니다. 확인한 TsidManipulator 구현은 한계값 설정과 task 등록을 분리합니다. 설정값이 특정 조건을 만족해야 addActuationTask 또는 addMotionTask가 호출됩니다.

따라서 “URDF에 한계가 있는데 왜 이 최적화에 적용되지 않았을까”를 조사할 때는 숫자만 볼 것이 아니라, 그 숫자를 담은 task가 실제 formulation에 추가되는 경로를 봐야 합니다. 여기서는 TSID의 공식 manipulator 예제를 따라 그 차이를 설명합니다.

같은 한계값을 만들고도 등록 여부는 달라진다

예제는 로봇 모델의 effortLimit에 tau_max_scaling을 곱해 최대 토크를 만들고, 그 음수를 최소 토크로 사용합니다. 이어 TaskActuationBounds 객체에 이 상·하한을 설정합니다. 여기까지는 최적화에 사용할 후보 task를 준비한 단계입니다.

그 다음 분기에서 w_torque_bounds > 0.0일 때만 그 객체를 addActuationTask로 등록합니다. 아래는 이 등록 흐름을 간추린 표기입니다.

URDF의 effortLimit → 배율 적용 → 토크 상·하한 설정

w_torque_bounds가 0이면:   이 bounds task를 등록하지 않음
w_torque_bounds가 양수면:  이 bounds task를 formulation에 추가

다른 설정은 같고 이 값만 0인 경우와 양수인 경우를 생각해 보세요. 두 경우 모두 메모리에 토크 한계값과 task 객체가 있을 수 있습니다. 하지만 그 객체가 이번 최적화 문제에 등록되는지는 다릅니다. 한계 배열을 출력해 올바른 숫자를 확인한 것만으로 등록 여부까지 확인한 것은 아닙니다.

최적화의 토크 제약과 드라이버의 출력 제한은 적용되는 위치가 다릅니다. 이 예제의 addActuationTask는 최적화 문제에 제약을 넣는 단계입니다.

가중치와 우선순위는 별도 인자다

이 코드에서 w_torque_bounds는 단순히 결과를 조금 더 중요하게 만드는 숫자로만 쓰이지 않습니다. 우선 0보다 큰지 검사해 등록 여부를 결정하고, 등록할 때는 task의 weight 인자로도 전달됩니다.

TSID의 formulation 선언을 보면 weight와 priorityLevel은 서로 다른 인자입니다. 확인한 manipulator 예제는 토크·관절 bounds task에 priority 0을, 자세와 말단 task에 priority 1을 전달합니다. 같은 weight 숫자를 쓴다고 이 등록 단계가 같아지는 것은 아닙니다.

자세와 말단 task도 별도로 등록됩니다. 원하는 관절 자세를 유지하는 목표와 손의 위치·방향을 맞추는 목표는 서로 경쟁할 수 있습니다. 전신 제어의 작업 우선순위를 이해한 뒤 이 코드를 읽으면, 목표를 얼마나 중시하는지와 어떤 한계 task가 문제에 포함됐는지를 나누어 볼 수 있습니다.

JointBounds라는 이름이 위치 한계를 뜻하지는 않는다

관절 쪽도 같은 등록 구분을 사용합니다. 예제는 velocityLimit에 배율을 적용한 뒤 TaskJointBounds에 속도 상·하한을 설정하고, w_joint_bounds > 0.0일 때만 motion task로 추가합니다.

여기서 이름보다 중요한 것은 현재 TaskJointBounds의 실제 계산입니다. 확인한 구현은 현재 속도와 시간 간격으로, 속도 한계가 요구하는 가속도 범위를 구합니다. 그 범위를 별도로 설정된 가속도 상·하한과 겹쳐 최종 가속도 제약으로 만듭니다. 이 compute 함수에서는 전달된 관절 위치 q를 사용하지 않습니다.

이 클래스가 계산하는 것은 속도와 가속도로부터 정한 가속도 범위입니다. URDF의 관절 위치 하한·상한을 적용하려면 위치 제한을 담당하는 task를 별도로 구성해야 합니다.

한 축의 속도 상한에서 나오는 가속도 상한은 다음 관계로 읽을 수 있습니다.

속도 한계가 허용하는 가속도 상한 = (속도 상한 − 현재 속도) / dt
최종 가속도 상한 = 위 값과 설정된 가속도 상한 중 작은 값

설명용으로 속도 상한을 1 rad/s, 현재 속도를 0.8 rad/s, dt를 0.1 s라고 놓으면 첫 번째 값은 2 rad/s²입니다. 별도의 가속도 상한이 1 rad/s²라면 그보다 작은 1 rad/s²가 최종 상한이 됩니다. 위 가상 입력을 소스의 식에 대입하면, 속도 여유보다 별도의 가속도 설정이 더 엄격한 제약으로 작용하는 경우를 볼 수 있습니다.

같은 속도 여유라도 dt가 달라지면 이 계산 결과는 달라집니다. 시간 간격은 단순 표시용 정보가 아니라 제약을 구성하는 입력인 셈입니다. 이 식으로 보면, 실제 제어 주기와 task의 시간 설정이 어긋날 때 한 주기에서 허용하려던 속도 변화와 계산한 가속도 범위가 맞지 않을 수 있습니다.

솔버의 크기는 등록된 문제를 따라간다

예제는 task를 구성하고 추가한 뒤 solver.resize(formulation.nVar, formulation.nEq, formulation.nIn)를 호출합니다. 솔버에 전달하는 크기가 로봇 관절 수 하나만으로 정해지는 것이 아니라, 현재 formulation이 가진 변수·등식·부등식 크기와 연결된다는 점을 보여 줍니다.

resize의 입력은 이미 등록된 formulation의 크기입니다. bounds 등록 분기를 건너뛰었다면 그 task가 빠진 문제의 크기가 전달됩니다. task를 동적으로 추가하거나 제거하는 응용에서는 변경 뒤 formulation과 solver 크기를 갱신하는 경로도 함께 확인해야 합니다.

URDF의 물리 한계, bounds 객체의 설정값, formulation에 등록된 task, 솔버가 낸 해는 서로 이어져 있지만 같은 것은 아닙니다. 해당 코드에서 토크가 제한되는 이유를 설명하려면 한계값의 출처와 등록 조건까지 연결해야 합니다. 계산된 토크가 실제 움직임으로 이어지는 문제는 역기구학과 역동역학의 차이에서 다룬 모델·운동 조건과 함께 봐야 합니다.

자료 확인: 2026년 10월 15일. TSID commit 25762a26464d10f6a2b1becc87f6e9a13c24c44b의 manipulator 예제, formulation 선언, joint-bounds 구현을 읽었습니다. 솔버를 실행하거나 실제 제약 만족·모터 응답을 시험하지 않았습니다.