mirror of
https://github.com/huggingface/lerobot.git
synced 2026-07-24 18:26:11 +00:00
feat(unitree_g1): optional camera streaming in onboard runner
Add --cameras to run_g1_onboard so view_cameras.py can connect. Reuses ImageServer + parse_camera_specs from run_g1_server; runs in a daemon thread independent of DDS/CAN. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -42,8 +42,9 @@ import threading
|
|||||||
|
|
||||||
import zmq
|
import zmq
|
||||||
|
|
||||||
|
from lerobot.cameras.zmq.image_server import ImageServer
|
||||||
from lerobot.robots.unitree_g1.config_unitree_g1 import UnitreeG1Config
|
from lerobot.robots.unitree_g1.config_unitree_g1 import UnitreeG1Config
|
||||||
from lerobot.robots.unitree_g1.run_g1_server import Gripper, build_gripper
|
from lerobot.robots.unitree_g1.run_g1_server import Gripper, build_gripper, parse_camera_specs
|
||||||
from lerobot.robots.unitree_g1.unitree_g1 import UnitreeG1
|
from lerobot.robots.unitree_g1.unitree_g1 import UnitreeG1
|
||||||
|
|
||||||
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s", force=True)
|
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s", force=True)
|
||||||
@@ -77,6 +78,13 @@ def main() -> None:
|
|||||||
"--gripper-no-fd", dest="gripper_fd", action="store_false", help="Classic CAN (non-FD adapter)"
|
"--gripper-no-fd", dest="gripper_fd", action="store_false", help="Classic CAN (non-FD adapter)"
|
||||||
)
|
)
|
||||||
p.set_defaults(gripper_fd=True)
|
p.set_defaults(gripper_fd=True)
|
||||||
|
# Optional camera streaming (so view_cameras.py can connect). Same spec format as
|
||||||
|
# run_g1_server.py: 'name:device[:WxH[:FOURCC]]' comma-separated.
|
||||||
|
p.add_argument("--cameras", default=None, help="Camera spec 'name:device[:WxH[:FOURCC]]', comma-sep")
|
||||||
|
p.add_argument("--camera-fps", type=int, default=30, help="Camera FPS")
|
||||||
|
p.add_argument("--camera-port", type=int, default=5555, help="Camera ZMQ port")
|
||||||
|
p.add_argument("--camera-width", type=int, default=640, help="Default camera width")
|
||||||
|
p.add_argument("--camera-height", type=int, default=480, help="Default camera height")
|
||||||
args = p.parse_args()
|
args = p.parse_args()
|
||||||
|
|
||||||
cfg = UnitreeG1Config(
|
cfg = UnitreeG1Config(
|
||||||
@@ -87,6 +95,16 @@ def main() -> None:
|
|||||||
gravity_compensation=args.gravity_compensation,
|
gravity_compensation=args.gravity_compensation,
|
||||||
cameras={},
|
cameras={},
|
||||||
)
|
)
|
||||||
|
# Optional camera server (for view_cameras.py). Runs in a background thread and is
|
||||||
|
# independent of DDS/CAN, so it coexists with the onboard controller and grippers.
|
||||||
|
camera_server = None
|
||||||
|
if args.cameras:
|
||||||
|
cameras = parse_camera_specs(args.cameras, args.camera_width, args.camera_height)
|
||||||
|
camera_server = ImageServer({"fps": args.camera_fps, "cameras": cameras}, port=args.camera_port)
|
||||||
|
threading.Thread(target=camera_server.run, daemon=True).start()
|
||||||
|
cam_summary = ", ".join(f"{name}(dev {c['device_id']})" for name, c in cameras.items())
|
||||||
|
logger.info("Camera server started on :%d: %s", args.camera_port, cam_summary)
|
||||||
|
|
||||||
robot = UnitreeG1(cfg)
|
robot = UnitreeG1(cfg)
|
||||||
logger.info("Connecting onboard robot (controller=%s)...", args.controller)
|
logger.info("Connecting onboard robot (controller=%s)...", args.controller)
|
||||||
robot.connect()
|
robot.connect()
|
||||||
@@ -156,6 +174,11 @@ def main() -> None:
|
|||||||
logger.info("Applied %d actions | axes=%s buttons=%s", n, axes, btn)
|
logger.info("Applied %d actions | axes=%s buttons=%s", n, axes, btn)
|
||||||
finally:
|
finally:
|
||||||
logger.info("Shutting down onboard controller...")
|
logger.info("Shutting down onboard controller...")
|
||||||
|
if camera_server is not None:
|
||||||
|
try:
|
||||||
|
camera_server.stop()
|
||||||
|
except Exception as e: # noqa: BLE001
|
||||||
|
logger.warning("Camera server stop failed: %s", e)
|
||||||
for g in grippers.values():
|
for g in grippers.values():
|
||||||
try:
|
try:
|
||||||
g.bus.disconnect()
|
g.bus.disconnect()
|
||||||
|
|||||||
Reference in New Issue
Block a user