Add leader-follower mode for SO101#314
Add leader-follower mode for SO101#314SinedYuk wants to merge 6 commits intoPositronic-Robotics:mainfrom
Conversation
d96a014 to
b0ab533
Compare
b0ab533 to
599b0b7
Compare
| def run(self, should_stop: pimm.SignalReceiver, clock: pimm.Clock) -> Iterator[pimm.Sleep]: | ||
| self.motor_bus.connect() | ||
| # Initialize kinematics in the subprocess (placo.RobotWrapper is not picklable) | ||
| _ = self.kinematic |
There was a problem hiding this comment.
Very hacky. Find a better way to achieve that. For example make _solve_ik and _forward_kinematics static methods that take kinematics as argument
| q_norm = self.rad_to_norm(qpos) | ||
| q_with_gripper = np.concatenate([q_norm, [self.target_grip.value]]) | ||
| self.motor_bus.set_target_position(q_with_gripper) | ||
| case roboarm_command.NormalizedJointPosition(qpos): |
There was a problem hiding this comment.
We cannot afford introducing new commands that eaily. The solution would be to convert normalised position into radians in the leader, and send this command to follower.
In other words, I want you to remove NormalizedJointPosition.
Moreover, I suppose that the leader should emit its state with the real joint values. This is the API that robotic arms use.
There was a problem hiding this comment.
Deleted the command
| self.robot_meta_in: pimm.SignalReceiver = pimm.FakeReceiver(self) | ||
|
|
||
| def run(self, should_stop: pimm.SignalReceiver, clock: pimm.Clock) -> Iterator[pimm.Sleep]: | ||
| from positronic.dataset.ds_writer_agent import DsWriterCommand, DsWriterCommandType |
There was a problem hiding this comment.
Import this on top level pls. BTW I know that claude has this tendency.
| from positronic.drivers.roboarm import command as roboarm_command | ||
|
|
||
|
|
||
| class LeaderFollower(pimm.ControlSystem): |
There was a problem hiding this comment.
QQ – do we really need to a separete ControlSystem. Can we just connect two SO101 arms together, where we will read positions from the first one and send them to the second one?
May be just adding passive=True to the arm driver. There's a question on how to enable data collection things, but it's a data collection script concern, not the driver's concern
There was a problem hiding this comment.
Added passive mode, reused keyboard control class, and extracted data collection logic into data_collection
Add leader-follower teleoperation for SO101
Adds
passivemode to the SO101 driver and a newmain_leader_followerdata collection entry point. Two physical SO101 arms connect via pimm signals: the leader (torques disabled) is moved by hand, and its joint positions are forwarded asJointPositioncommands to the follower in real-time.Changes
positronic/drivers/roboarm/so101/driver.pypassiveparameter toRobot— disables torque, skips command processing, runs at 100 Hz (vs 1000 Hz for active)commandsandtarget_gripreceivers are only created whenpassive=FalseKinematicsinitialization intorun()(placoRobotWrapperis not picklable)_solve_ik,_forward_kinematics,_norm_to_rad,_rad_to_normas module-level functionspositronic/data_collection.pymain_leader_follower()— wires leader → follower viapimm.map, records dataset with keyboard controls (s= start,p= stop,q= quit)so101_leaderCLI config withso101_passiveleader armbg_cslist inmain()(robot_arm and gripper are the same object for SO101)positronic/cfg/hardware/roboarm/__init__.pyso101_passiveMisc updates:
LinuxVideodriver — Recording episodes now works correctly with USB cameras (Arducam, Sonix, etc.)sonixcamera config for USB2.0 CAM1so101_leaderdefault camera — Now uses Sonix camera out of the boxUsage
uv run positronic-data-collection so101_leader --output_dir=~/datasets/so101_runs