서보 드라이브 실기 근거
내 서보 드라이브 붙이기 의 동작 가운데 무엇을 실제 드라이브로 확인했고 무엇을 확인하지 않았는지 적습니다. 결과, 조건, 남은 항목의 정본은 OSS 저장소의 docs/getting-started.md Hardware evidence 이고, 이 페이지는 그 요약입니다.
기록은 두 경로에서 나왔습니다.
- 은퇴한
drive_node경로. 그 위의 이전 벤치와 함께 다섯 세션 돌렸습니다. 위치(csp) 축은 A6B 두 대로 돌렸고, 속도(csv)·토크(cst) 축은 짧은 저속 런만 돌렸습니다. - 지금의
plem drive run경로. 같은 장비에서 실기 세션 여러 번 돌렸습니다. 조로 묶은 A6B 위치 축 두 개에 두 프로파일의 이동과 정지, 벤치와 승인, 그 파일의csv사본에 스트림과 끊김,cst사본에 토크 전달과 끊김 정지입니다.--rt-cpu, 두 버스는 아직 돌리지 않았습니다.
하드웨어 없는 시험
지금 경로는 하드웨어 없는 시험으로도 덮여 있습니다.
- 단위 시험:
run이 쓰는 로봇 설명과 파라미터, 하드웨어 플러그인, 스트림 컨트롤러, 가짜 버스에 붙인 Python 클라이언트, 프로파일(S-커브는 Ruckig 라이브러리와 대조). - 컨트롤러 매니저에 SIGINT 를 보내, 버스가 정지 요청을 먼저 받고 그다음 멈추는지 보는 시험.
- 모의 호스트 위에서 전체 사슬을 도는 종단 시험: 해제, 두 프로파일의 이동, 정지, 재해제, 복귀, 그리고 속도·토크 축에 흘리는 스트림.
- 가짜 버스 위의 벤치 시험.
정지의 근거
멈춥니다 표의 각 행을 얼마나 믿을 수 있는지입니다.
| 상황 | 근거 |
|---|---|
| 스트림 끊김 | 이전 경로에서 명령 감시가 100 ms 이던 때 토크 축으로 쟀습니다. 마지막 명령 뒤 0.100 s 에 끊김을 감지하고, 다음 제어 주기(1 ms)에 체결을 지시했습니다. 지금의 50 ms 는 지금 경로의 세션에서 쟀습니다. 끊김 이벤트(0x54)의 나이는 0.051 s 였고, 10 rad/s 에서 끊은 스트림은 축을 세웠습니다. 정확한 지연은 이미 멈춘 버스에서만 쟀습니다 |
| 한계를 넘는 명령 | A6B 실기에서 범위 밖 위치 명령(사유 4)과 한계 위 csv 속도 명령(사유 6)은 적용되지 않고 두 축에 LIMIT_STOP 이 났습니다. 실측 속도가 한계를 넘은 정지(사유 2)도 봤습니다 |
| Ctrl-C (SIGINT) | 정지 요청이 버스를 놓기 전에 오는 순서는 시험이 핀합니다. 은퇴한 노드에서는 SIGINT, SIGTERM 모두 실기로 쟀습니다(종료 코드 0, 해제 중 SIGTERM 에 모든 브레이크 체결 확인). ros2_control 경로에서는 움직이던(약 9.9 rad/s) 위치 축 두 개가 196 ms 만에 감속해 서고 브레이크 체결이 확인됐습니다(한 세션) |
| SIGKILL | 드라이브가 동작 상태를 떠난 것만 봤습니다. A6B 두 대는 해제 중 SIGKILL 에 스스로 fault(0x603F = 0xFF58)가 됐습니다. 시간도 체결 시각도 재지 않았습니다. 보장으로 읽지 마세요 |
protective_stop | 곧바로 disable 하던 때 실기로 쟀습니다. 체결을 확인했고 보호정지가 걸리지 않아 reset 없이 다시 해제됐습니다. 감속해 세우는 지금의 요청 정지는 한 세션에서 11.9, 17.9 rad/s 위치 축을 평균 61, 68 rad/s²(한계 95)로 세웠습니다. Quick stop 대비 단계까지 간 적은 없습니다. 벤치의 stop-decel 항목이 이 감속을 잽니다 |
이전 경로의 기록
Panasonic A6B 두 대(MDDLN55BE, 모터 MSMF102L1H5)에서 주로 위치 csp 축으로 다섯 세션 돌렸습니다. 그때의 소프트웨어는 protective_stop 을 곧바로 disable 했고(지금 한계와 명령 끊김이 타는 경로) 명령을 100 ms 로 감시했습니다. 아래의 보호정지 수치는 그 곧바로 서는 경로의 근거입니다.
모터마다 21:1 유성 감속기가 달려 있고, 두 감속기 출력축은 마주 봅니다. 조로 묶은 것은 결합 시험에서만입니다. 결합 시험 전까지 파일은 gear_ratio: 1.0 이라 그 수치는 모터축 값입니다.
운동과 정지.
- RT 램프(은퇴)로 낸 왕복 5~366 rad/s(모터축)가 알람 없이 따랐습니다.
- 공장 감속값에서는 움직이는 축의 보호정지가 감속하지 않아, A6B 예제가 정지 감속을 씁니다.
- 3200 rad/s²(모터축, 무부하 벤치에서 고른 값이라 설비에 맞게 정합니다)에서 약 344 rad/s 의 보호정지는 0.19 s, 29.7 rad 만에 0.05 rad/s 아래로 내려갔습니다.
- 저속에서는 감속을 올려도 거리가 덜 줄었습니다. 46 rad/s 에서 640 → 3200 rad/s² 로 바꾸면 3.4 → 1.8 rad 였습니다. 정지 요청 뒤 16~45 ms 동안 속도가 처음의 95 % 위에 머물렀습니다.
명령, 한계, 신호.
- 위치 명령 범위 밖과
csv속도 명령 초과는 적용되지 않고LIMIT_STOP이 났습니다. - 위치 한계 밖에서 기동한 축은 해제와
reset_error가 거부됐습니다. - 해제 중 SIGTERM 은 종료 코드 0 이었습니다. 해제 중 SIGKILL 에서는 두 드라이브가 스스로 fault 상태가 됐습니다(0x603F = 0xFF58). 멈추는 시간과 브레이크 시각은 재지 않았습니다.
- 끝 없는 축(
limits.position: null)은 32비트 카운트 경계를 넘어 연속으로 돌았습니다. 다만 한 방향으로 모터 약 2,900 회전을 간 뒤에는 다음 기동에서 두 드라이브가 다회전 절대 위치 넘침 알람(Err29.1)으로 기동하지 않았습니다. 드라이브 쪽 설정 문제입니다(끝 멈춤이 없는 축).
벤치와 승인. 이전 벤치(drive_node 위)가 그때의 필수 항목을 통과해 승인 파일을 썼습니다. 그 파일은 벤치 모드 없이 기록 대조를 통과한 뒤 reset_error 한 번으로 해제하고 움직였습니다. 대조는 시리얼과 revision number 로 했습니다. 버전 문자열은 공백과 NUL 바이트가 섞여 기록에 담기지 않았습니다.
동기. sm 에서 A6B 는 Free Run 으로 돌았습니다. dc_sync0 는 0x1C32:02 를 손으로 쓴 구성에 이어, PLEM 이 쓰는 구성(dc.write_cycle_time, 지금 A6B 예제)에서도 OP 에 들어 enable 뒤 새 드라이브 fault 없이 추종했습니다.
SYNC0 shift.
sync0_shift_ns: 0에서는 SYNC0 이 그 주기의 프레임보다 먼저 울렸습니다. 보내는 목표는 고른데 드라이브의 위치 요구값(0x6062)이 가끔 한 주기를 건너뛰거나 되풀이했고, 1~2 주기 뒤 토크가 튀었습니다(출력축 최대 약 25 N·m).- 번갈아 돌린 런에서 0 은 10 번 중 6 번, 300 µs 는 0 번 나타났고, 20 µs 에서도 나타났습니다. A6B 예제는 이제
300000입니다(SYNC0 shift). - 출력축을 묶고 출력축 23.5 rad/s 로 돈 런은 300 µs 에서 31 s 를 결합 가드 정지 없이 마쳤습니다(0 에서는 약 10 s 에 정지). 다만 약 300 µs 의 DC 편차가 튄 동안 짧은 토크 튐이 있어 그 편차 문제는 남아 있습니다.
- 결합 런은 한 번이고, 1 ms 밖의 주기는 돌리지 않았습니다.
감속기 출력축 결합과 gear_ratio: 21.0. 두 출력축을 조(jaw)로 묶고 A6B 예제를 gear_ratio: 21.0(값은 출력축)으로 바꿔 벤치하고 승인했습니다. 그 뒤 ±2 rad 데모가 출력축 17.3 rad/s 순항까지 enable 뒤 fault 없이 돌았습니다. 그 전에 ±0.5 rad 런은 앞선 시험 파일이 드라이브 RAM 에 남긴 값 탓에 위치 편차 알람(Err24.0)으로 멈췄고, 전원을 다시 넣은 뒤 돌았습니다(startup_sdo). 서로 미는 토크는 최대 4.6 N·m, 두 축 위치 차는 최대 0.007 rad 였습니다. 조의 하중을 잰 것은 아닙니다.
노드 종료(drive_node). 정상 종료 뒤 드라이브에 통신 알람(Err80.4)이 자주 남았습니다. 노드가 OP 이탈을 EtherCAT 마스터(IgH 1.6.13)에 맡기는데, 그 전이가 드라이브의 100 ms 감시보다 길어질 때입니다. 정지 런에서는 축이 멈추고 브레이크가 물린 뒤의 알람이었습니다. 다음 기동에서 reset_error 로 풉니다.
그 밖의 드라이브. Neuromeka Indy7 v2 드라이브 벤치를 두 번 돌렸습니다(한 축과 두 축, 코드로 조립한 모델, 선언 매핑, 0 Nm 만, sm). 위 표의 토크 축 실측(명령 끊김)은 그 구성에서 잰 값입니다.
지금 경로의 기록
같은 A6B 두 대, dc_sync0, 1 ms 주기로, 감속기 출력축을 조로 묶었습니다. 파일은 A6B 예제에서 축 1 의 아래 위치 한계만 −46 rad 로 바꾼 사본입니다. 결합 장비에서 벤치의 두 축이 서로 반대로 움직이게 하려는 것입니다. 모든 이동은 두 축을 거울 대칭으로 명령했습니다. 서로 미는 토크 12 N·m(출력축)나 위치 차 0.03 rad 에서 정지하는 감시는 한 번도 걸리지 않았습니다(최대 2.9 N·m, 0.014 rad). 조의 하중을 잰 것은 아닙니다.
- 처음 소프트웨어에서 드러나 고친 결함 두 개. 벤치
stop-decel의 순항(이 파일에서 4.87 rad/s, 51 ms)이 드라이브 응답 지연(약 33 ms)보다 짧아, 순항의 89 % 에서 정지를 요청해 항목이 실패했습니다. 또 명령의 시각을 컨트롤러가 계획한 순간이 아니라 쓴 순간으로 찍어, 속도 한계의 0.9 배에서 한 주기 목표 이동이 0.0201 rad(한계 0.0199)가 되어LIMIT_STOP(사유 5)이 났습니다. RT 루프는 제때 돌았습니다. - 벤치와 승인. 고친 소프트웨어에서 벤치는 필수 항목을 모두 통과해 승인 기록을 파일에 남겼습니다.
stop-decel은 3.62 rad/s 중 3.48 에서 요청해 0.100 s 뒤 정지, 34.7 rad/s² 였습니다. 끝 점검의 0x6064 이동(모터 1.16 회전)과 bit 13 경고는 벤치 자신의 이동 때문입니다. 지금 벤치는 둘 다 경고하지 않습니다. - 승인 파일로 긴 런. 승인 파일을
--unapproved없이 띄워 10분 동안 속도 한계의 0.9 배(17.9 rad/s)로 −14 ↔ 26 rad 를 Trapezoid, SCurve 번갈아 230 번 이동했습니다. 전부 도착했고LIMIT_STOP, 보호정지, 주기 누락은 0 이었습니다. 주기 편차는 최대 11.8 µs, 처음 20 s 뒤 DC 편차는 최대 5.4 µs 였습니다. - 이동, 해제, 정지. 시각 수정 전 소프트웨어(정지와 해제 경로는 같음)에서
--unapproved로 돌렸습니다. 두 프로파일의 ±3 rad 이동은 1 mrad 안에 도착했고, 해제 때 움직임은 최대 5.2 µrad 였습니다.Bus.stop()은 11.93, 17.88 rad/s 에서 평균 61, 68 rad/s² 로 세우고 0.21, 0.35 s 에 모든 브레이크 체결로 돌아왔습니다. 움직이던 중 SIGINT 는 196 ms 감속 정지, 브레이크 확인, 마스터 Idle, 잔존 프로세스 없음이었습니다. csv사본의 스트림.Bus.stream은 25 rad/s(한계 19.9), NaN, 축 누락을 보내지 않고 거부했습니다. 3 rad/s 런에서는 200 Hz 로 흘리던 Python 루프가 한 번 126 ms 멈춰 축이 먼저 섰습니다. 그래서 스트림 루프는 가볍게 둡니다. 그 뒤의 끊김은 이미 보호정지한 버스에서 마지막 호출 약 107 ms 뒤 끊김 이벤트(0x54, 나이 0.051 s)를 냈습니다. 움직이는 축은 10 rad/s 에서 끊은 스트림으로 섰습니다. 클라이언트는 약 190 ms 안에 정지를 봤고, RT 쪽 시각은 기록하지 못했습니다. 끊김 정지 뒤reset_error는 스트림이 다시 살아 있을 때만 풀렸습니다.
그 뒤의 기록
같은 장비, 같은 파일과 그 사본으로 그 세션 뒤의 소프트웨어(plem.drive 의 즉시 실패 검사, 100 Hz joint_states, rt_raw 로 재는 벤치)에서 돌렸습니다.
- 클라이언트 쪽 실패와 상태 주기. 끊긴 스트림으로 보호정지가 선 채 부른
release()와reset()은 0.20 s, 0.08 s 에 끊긴 스트림을 지목해 실패했습니다(전에는 15 s 시간 초과). fault 가 래치된 축(fault_reset.policy: none)에서 부른release()는 요청 전에 실패했고,reset()뒤 해제됐습니다.joint_states는 100.2 Hz 였고, 벤치의stop-decel은rt_raw의 주기를 잃지 않고 34.2, 34.8 rad/s² 로 통과해 승인됐습니다. 부하 평균 14.6(바쁜 루프 11개로 만든 부하)인 기기에서 200 Hzcsv스트림 60 s 는 185 Hz 를 유지했고(가장 긴 간격 13.4 ms), 보호정지는 없었습니다. cst사본의 토크. 위치 축 파일을cst(0x6071, 토크 한계 ±3 N·m)로 바꾼 사본입니다.Bus.stream은 3.5 N·m 를 보내지 않고 거부했습니다. 거울 대칭 1.5 N·m 펄스는 적용 1.47 N·m, 실측 +1.54/−1.60 N·m 였지만 움직이지 않았습니다(3 µrad). 정지 마찰로 보이나 원인은 확정하지 않았습니다. 축당 3 N·m 에서 한 축은 결합 유격(약 0.09 rad) 안에서 돌았고, 결합한 두 축이 함께 돌지는 않았습니다. 스트림을 끊으면 보호정지가 서고 곧바로 disable, 브레이크 체결이었습니다. 스트림을 다시 보낸 뒤reset(),release()가 됐고,Bus.stop()은 곧바로 disable 해 체결했습니다.- 기동 대기와 A6B 예제
demo.py. 버스는 모든 슬레이브가 OP 에 든 뒤에 떴고(루프 시작 뒤 2.4~5.8 s), OP 를 기다리는 중의 Ctrl-C 는 0.38 s 에 정지했습니다.demo.py(±5 rad 10 번, 한계의 0.4 배)는 결합한 두 축에서 Trapezoid 33.0 s, SCurve 35.4 s 에 끝났고, 약 5 ms 마다 잰 최대 위치 불일치는 0.00215, 0.00212 rad, 최대 속도 합은 0.156, 0.143 rad/s 였습니다(각 1 회). 보호정지, 주기 누락, 오버런은 0 이었습니다.plem drive run --plot의 PlotJuggler 는rt_raw/samples를 그렸습니다(처음엔 PlotJuggler ROS 2 플러그인이 상한 배열을 고정 배열로 읽어 "decode: not enough data" 로 실패했고,RtRawBatch배열을 상한 없이 바꿔 고쳤습니다). 실제 위치는 명령보다 늦게 따라갔습니다(이 런에서는 재지 않음. getting-started 10절 "The example's values and where they come from": 약 32 ms × 속도, 그중 명령 필터가 약 16 ms).
DC 동기 편차
A6B 두 대에서 dc_sync_monitor_ns(IgH 의 상한 추정치)로 본 값입니다.
- 기동 직후 15
30 s 동안 1 ms 가까이까지 보였고, 그 뒤 p50 0.10.3 µs, 최대 약 1.5 µs 로 가라앉았습니다. EtherCAT 마스터 IgH 1.6.13 은 운전 전에 클록을 따로 맞추는 과정이 없습니다. - 가라앉은 뒤에도 튈 수 있습니다. 90 s 넘는 런마다 약 110 s 주기로 수백 µs 까지 올랐다가 10~30 s 안에 내려왔습니다. 그동안 주기 누락과 드라이브 fault 는 없었습니다. 원인은 모릅니다.
- PLEM 은 이 정착을 기다리지 않습니다.
실기로 확인하지 않은 것
- 토크로 결합한 두 축을 함께 돌리는 것, 1.5 N·m 를 넘는 토크의 명령·실측 대조
csv사본 하나 밖의 스트림--rt-cpu, 두 버스- 스트림 축이 있는 버스의 벤치(
stale-engage) - 요청 정지의 Quick stop 대비(
HALT_TIMEOUT)와 A6B 예제의 605Ah = 2 example_drive.yaml그대로pdo_mapping: scanned,pdo_mapping: standard- 짧은 저속 런 밖의
cst, 고속csv축의 보호정지, 움직이는cst축의 끊김 정지 - 움직이던 축의 실측 위치가 한계를 넘는 보호정지
- 자동 fault reset(
bounded_auto_reset)과 0 이 아닌pulse_ms - 한 드라이브의 여러 축, 세 대 이상
- A6B 밖의 드라이브의 DC
- 1 ms 밖의 주기에서의 SYNC0 shift 와 드라이브 쪽 입력 shift(0x1C33)
- A6B 두 대 밖에서의 벤치와 승인 경로
- 조가 하중을 실은 결합 구동
- 위치·속도 축에서 브레이크를 연 뒤 드라이브 루프가 하중을 버티는 거동
- PlotJuggler 의 header stamp 선택(GUI 에서 확인하지 않음)