SparkSim: MAXMotion, kCos, and Slots
Maintainer thường phản hồi trong vòng 1 ngày
Chưa có ai nhận issue này.
Đánh giá
- Độ khó
- 4/5
- Thời gian dự kiến
- 3-5 ngày
- Mức phù hợp với người mới
- 22/100
Hướng nghiên cứu
Start with the linked test project at yamg revlib-sim-tests and run ./gradlew test; the failing tests map to the three reported bugs: MAXMotion position tracking, kCos using the setpoint instead of the position, and kV ignored outside closed-loop slot 0. Each bug is reproduced with the same setup (P, I and D at 0 except the gain under test, 12 V bus, iterate every 20 ms, sim.enable()). Done means those tests pass against the documented behaviour, with the SparkSim changes kept separate for each bug.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
While building simulation support for SPARKs in YAMS and YAMG I ran into a few problems with SparkSim's closed loop. Each one was tested on its own: P, I and D at 0, only the gain under test set, 12 V bus, iterate() every 20 ms, and the SPARK enabled with sim.enable().
Versions: WPILib / GradleRIO 2027.0.0-alpha-7, REVLib 2027.0.0-alpha-8 (first found on 2027.0.0-alpha-7; alpha-8 gives exactly the same results), Java, desktop simulation. The SPARK MAX and SPARK Flex behave the same.
The snippets below all use this setup:
HAL.initialize();
SparkMax spark = new SparkMax(CANPort.CAN_S0, 1, MotorType.kBrushless);
SparkMaxConfig config = new SparkMaxConfig();
config.closedLoop.pid(0, 0, 0);
// ... the gain under test ...
spark.configure(config, ResetMode.kResetSafeParameters, PersistMode.kNoPersistParameters);
SparkMaxSim sim = new SparkMaxSim(spark, DCMotor.getNEO(1));
sim.enable(); // outputs nothing while disabled otherwise
// "Held still": the output ramps in over ~20 steps, so read it after 50.
for (int i = 0; i < 50; i++) {
sim.iterate(0, 12, 0.02);
}
double volts = sim.getAppliedOutput() * 12;
1. MAXMotion position doesn't follow its profile in simulation
I ran a NEO through DCMotorSim (J = 0.0005 kg m², 1:1):
config.closedLoop.pid(0.0002, 0, 0);
config.closedLoop.feedForward.kV(1.0 / 473); // a NEO's kV, V/RPM
config.closedLoop.maxMotion.cruiseVelocity(3000).maxAcceleration(6000);
// configure, create the sim, enable...
spark.getClosedLoopController().setSetpoint(100, ControlType.kMAXMotionPositionControl);
DCMotorSim physics = new DCMotorSim(Models.singleJointedArmFromPhysicalConstants(DCMotor.getNEO(1), 0.0005, 1), DCMotor.getNEO(1));
for (int i = 0; i < 250; i++) { // 5 s
physics.setInputVoltage(sim.getAppliedOutput() * 12);
physics.update(0.02);
double rpm = physics.getAngularVelocity() * 60 / (2 * Math.PI);
sim.iterate(rpm, 12, 0.02);
}
With 6000 RPM/s it should take 0.5 s to reach 3000 RPM and land on 100 rotations at about 2.5 s. What I got:
| t | position | velocity |
|---|---|---|
| 0.5 s | 0.7 rot | 267 RPM |
| 1.0 s | 4.9 rot | 731 RPM |
| 2.0 s | 25.6 rot | 1752 RPM |
| 3.0 s | 64.5 rot | 2942 RPM |
| 4.0 s | 114.5 rot | 2873 RPM |
| 5.0 s | 157.9 rot | 2337 RPM |
So it accelerates at roughly 1000 RPM/s rather than 6000, and then runs straight past the goal without stopping (still moving at 158 rotations). Ten times the kP gives almost the same numbers. Raising maxAcceleration to 36000 gets it to cruise faster, but it then overshoots to ~112 rotations and oscillates around the goal.
For comparison, plain kVelocity to 3000 RPM with the same kV settles at 3024 RPM in under 0.5 s, so the motor model and kV are fine. It's the MAXMotion profile in the sim:
spark.getClosedLoopController().setSetpoint(3000, ControlType.kVelocity); // 3024 RPM after 1 s
Because of this we currently have to run a TrapezoidProfile on the SystemCore in simulation and send each step with kPosition.
For reference: the feedforward that does work
Each gain on its own, held still (expected / measured):
| mode | gain | expected | measured |
|---|---|---|---|
| kVelocity | kV 0.002 V/RPM at 1000 RPM | 2 V | 2.0000 V |
| kVelocity | kS 0.5 V | 0.5 V | 0.5000 V |
| kPosition | kS 0.5 V | ±0.5 V (sign of the error) | ±0.5000 V |
| kPosition | kG 1 V | 1 V | 1.0000 V |
| kMAXMotionPositionControl | kG 1 V | 1 V | 1.0000 V |
| kMAXMotionPositionControl | kA 0.001 V/(RPM/s) at 2000 RPM/s | 2 V | 2.0000 V |
2. kCos uses the setpoint, read as degrees, rather than the position
The docs say kCos "is multiplied by the cosine of the absolute position of the mechanism". In the sim it looks like it uses the setpoint, treated as degrees, and ignores where the mechanism actually is:
config.closedLoop.feedForward.kCos(1.0).kCosRatio(1.0);
// configure, create the sim, enable...
sim.setPosition(0.25); // a quarter turn: cos(position) = 0
spark.getClosedLoopController().setSetpoint(0.25, ControlType.kPosition);
// held still for 50 steps: 1.0000 V, which is cos(0.25 degrees), not cos(a quarter turn) = 0
With the motor held still at each position:
| motor at | setpoint | output | cos(position, rotations) | cos(setpoint, degrees) |
|---|---|---|---|---|
| 0 rot | 0.25 | 1.0000 V | 1 | 1.0000 |
| 0 rot | 0.5 | 1.0000 V | 1 | 1.0000 |
| 0 rot | 90 | 0.0000 V | 1 | 0 |
| 0 rot | 180 | -1.0000 V | 1 | -1 |
| 0.25 rot | 0.25 | 1.0000 V | 0 | 1.0000 |
| 0.5 rot | 0.5 | 1.0000 V | -1 | 1.0000 |
The output always matches cos(setpoint in degrees) and never cos(position). So an arm with kCosRatio = 1 / gearRatio (position in rotations) gets close to the full kG at every angle in simulation. We're working around it by setting kCos to 0 in simulation and sending the gravity as an arbitrary feedforward:
config.closedLoop.feedForward.kCos(RobotBase.isSimulation() ? 0 : kG).kCosRatio(1.0 / gearRatio);
// ...
double gravity = RobotBase.isSimulation() ? kG * Math.cos(armAngleRadians) : 0;
spark.getClosedLoopController().setSetpoint(
setpointMotorRotations, ControlType.kPosition, ClosedLoopSlot.kSlot0, gravity, ArbFFUnits.kVoltage);
3. kV only works in closed loop slot 0
Found this one while putting the test project together. The same gain in each slot, kVelocity at 1000 RPM, motor held still:
config.closedLoop.feedForward.kV(0.002, ClosedLoopSlot.kSlot1);
// configure, create the sim, enable...
spark.getClosedLoopController().setSetpoint(1000, ControlType.kVelocity, ClosedLoopSlot.kSlot1);
// held still for 50 steps: 0 V; the same in kSlot0 gives 2.0000 V
| gain | slot 0 | slot 1 | slot 2 | slot 3 | expected |
|---|---|---|---|---|---|
| kV 0.002 V/RPM | 2.0000 V | 0 V | 0 V | 0 V | 2 V |
| kS 0.5 V | 0.5000 V | 0.5000 V | 0.5000 V | 0.5000 V | 0.5 V |
| kP 0.0001 /RPM | 1.2000 V | 1.3000 V | 1.2000 V | 1.2000 V | 1.2 V |
So kV is ignored in slots 1 to 3, and kP in slot 1 comes out about 8% high. I ran into it because a velocity mode on slot 1 only got to 1742 RPM out of 3000.
Test project
Everything above is in a small robot project you can run: https://github.com/Yet-Another-Software-Suite/yamg/tree/revlib-sim-tests/revlib-sim-tests
./gradlew simulateJavaand a gamepad on port 0 lets you run a slide and an arm with MAXMotion / kCos, or with the workaround, and compare them in AdvantageScope or Glass. The README lists the buttons and what's published../gradlew testruns a test for each of these. They check the documented behaviour, so the ones that fail are the bugs above.
I haven't been able to check whether a real SPARK does the same; this is all in simulation.
- Ngôn ngữ chính
- Java
- Star
- 194
- Fork
- 26
- Merge trung bình
- 19 giờ 11 phút
- Pull request đã merge (30 ngày)
- 14
Chuẩn bị môi trường
Dự án này không cung cấp dev container, Dockerfile hay hướng dẫn đóng góp, nên bạn cần tự thiết lập môi trường: hãy bắt đầu từ README và xem hướng dẫn đóng góp lần đầu của chúng tôi để biết các bước chung.
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của wpilibsuite/SystemcoreTesting
-
Romi Reference ExampleĐang mở
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 78/100
wpilibsuite/SystemcoreTesting#380 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Limelight Pending
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 78/100
wpilibsuite/SystemcoreTesting#315 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 3/5 Nửa ngày Mức phù hợp với người mới 45/100
wpilibsuite/SystemcoreTesting#427 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 3/5 1-2 ngày Mức phù hợp với người mới 40/100
wpilibsuite/SystemcoreTesting#424 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 3/5 1-2 ngày Mức phù hợp với người mới 45/100
wpilibsuite/SystemcoreTesting#423 ·
Maintainer thường phản hồi trong vòng 1 ngày
Tất cả issue của wpilibsuite/SystemcoreTesting
Issue tương tự
-
enhancement Improvement
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 76/100
zowe/zowe-client-java-sdk#647 ·
-
NullPointerException when a `DeserializationProblemHandler` returns `null` for `byte[]` (single value unwrapped) and `char[]` elementsCó thể đã có người làm @Marti2203 đã nhận hôm nay. Đang mở
Độ khó 2/5 Nửa ngày Mức phù hợp với người mới 88/100
FasterXML/jackson-databind#6300 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
content good first issue
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 95/100
cpinitiative/usaco-guide#6699 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
BC remittance upload: a claim MSP refused (S03) is saved with a blank status instead of refused (F)Có thể đã có người làm Có pull request liên kết đang mở hoặc đã được merge. Đang mởtype: bug
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 88/100
carlos-emr/carlos#4933 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
bug
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 82/100
apache/rocketmq-dashboard#6925 ·
Maintainer thường phản hồi trong vòng 3 ngày